package com.jrx.anytxn.customer.handel.impl;

import com.google.common.collect.Maps;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.exception.TxnRuntimeException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.JsonUtils;
import com.jrx.anytxn.common.uitl.SeqUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.bean.CreditApprovalBean;
import com.jrx.anytxn.customer.bean.InvestorCreditBean;
import com.jrx.anytxn.customer.bean.MarketingRecordCreationBean;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.constant.ErrorMsgConstant;
import com.jrx.anytxn.customer.dto.creditinfo.CreditActiveReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditAdjustInfoReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditAdjustReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditAllInfoQueryReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditAllInfoQueryRes;
import com.jrx.anytxn.customer.dto.creditinfo.CreditApprovalReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditCancelReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditFreeze;
import com.jrx.anytxn.customer.dto.creditinfo.CreditFreezeOfCmsReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditFreezeReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfo;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoQueryReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoQueryRes;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoRes;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoTree;
import com.jrx.anytxn.customer.dto.customer.CreditReferenceReq;
import com.jrx.anytxn.customer.dto.customer.CreditReferenceRes;
import com.jrx.anytxn.customer.dto.customer.CustomerFirstLevelRes;
import com.jrx.anytxn.customer.dto.customer.CustomerLimitAdjustRes;
import com.jrx.anytxn.customer.dto.customer.CustomerLimitInfoRes;
import com.jrx.anytxn.customer.dto.customer.CustomerSecondLevelRes;
import com.jrx.anytxn.customer.entity.CmCustomerAddressInfo;
import com.jrx.anytxn.customer.entity.CmCustomerExtInfo;
import com.jrx.anytxn.customer.entity.CmCustomerFirstLevel;
import com.jrx.anytxn.customer.entity.CmCustomerLimitFroze;
import com.jrx.anytxn.customer.entity.CmCustomerLimitInfo;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.entity.CmLimitAdjustLog;
import com.jrx.anytxn.customer.entity.CmLimitChangeLog;
import com.jrx.anytxn.customer.entity.TlMarketingRecord;
import com.jrx.anytxn.customer.handel.ICustomerLimitInfoHandler;
import com.jrx.anytxn.customer.mapper.base.CmLimitAdjustLogMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerAddressInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerExtInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerLimitInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerSecondLevelMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmLimitAdjustLogMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmLimitChangeLogMapper;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.customer.service.impl.ICmCustomerLimitFrozeServiceImpl;
import com.jrx.anytxn.feign.api.request.LimitLogoutRequest;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.feign.gateway.bean.ApiResponse;
import com.jrx.anytxn.feign.transaction.request.BillReq;
import com.jrx.anytxn.feign.transaction.request.EtlSettleReq;
import com.jrx.anytxn.feign.transaction.response.AllRemainBillQueryRes;
import com.jrx.anytxn.feign.transaction.response.EtlSettleRes;
import com.jrx.anytxn.feign.transaction.service.IAnyTxnTransactionService;
import com.jrx.anytxn.i18n.response.TxnRespResult;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.dto.aviator.RuleMatchReq;
import com.jrx.anytxn.param.dto.limit.CreditLimitMappingRes;
import com.jrx.anytxn.param.dto.limit.PrCreditLimit;
import com.jrx.anytxn.param.entity.BtLimitAdjustInfo;
import com.jrx.anytxn.param.entity.PrChannelTable;
import com.jrx.anytxn.param.entity.PrCreditLimitMapping;
import com.jrx.anytxn.param.entity.PrInterestTable;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.enums.BizTypeEnum;
import com.jrx.anytxn.param.mapper.ext.ExtPrCreditLimitMappingMapper;
import com.jrx.anytxn.param.mapper.ext.ExtPrOrganizationTableMapper;
import com.jrx.anytxn.param.service.aviator.IAviatorService;
import com.jrx.anytxn.param.service.limit.ICreditLimitMappingService;
import com.jrx.anytxn.param.service.limit.ICreditLimitTemplateService;
import com.jrx.anytxn.param.service.limit.ICreditLimitTypeService;
import com.jrx.anytxn.param.service.product.IInterestTableService;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.IChannelTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import com.jrx.anytxn.ribbon.config.GrayVersionConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Component
public class CustomerLimitInfoHandlerImpl implements ICustomerLimitInfoHandler {

    private static Logger logger = LoggerFactory.getLogger(CustomerLimitInfoHandlerImpl.class);

    @Autowired
    private IChannelTableService iChannelTableService;

    @Autowired
    private IOrganizationTableService iOrganizationTableService;

    @Autowired
    private ICustomerService iCustomerService;

    @Autowired
    private ICreditLimitMappingService iCreditLimitMappingService;

    @Autowired
    private ICreditLimitTemplateService iCreditLimitTemplateService;

    @Resource
    private ICreditLimitTypeService iCreditLimitTypeService;

    @Autowired
    private ICustomerLimitInfoService iCmCustomerLimitInfoService;

    @Autowired
    private ISegmentService iSegmentService;
    @Resource
    private ExtCmCustomerLimitInfoMapper extCmCustomerLimitInfoMapper;

    @Resource
    private CmLimitAdjustLogMapper cmLimitAdjustLogMapper;

    @Resource
    private ExtCmLimitAdjustLogMapper extCmLimitAdjustLogMapper;

    @Autowired
    private IOrganizationTableService organizationTableService;

    @Autowired
    private IProductTableService productTableService;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @Resource
    private GrayVersionConfig grayVersionConfig;

    @Resource
    private ExtCmCustomerAddressInfoMapper extCmCustomerAddressInfoMapper;

    @Resource
    private ExtCmCustomerSecondLevelMapper extCmCustomerSecondLevelMapper;

    @Resource
    private ExtCmLimitChangeLogMapper extCmLimitChangeLogMapper;

    @Resource
    private ExtPrCreditLimitMappingMapper extPrCreditLimitMappingMapper;

    @Resource
    private ExtPrOrganizationTableMapper extPrOrganizationTableMapper;

    @Lazy
    @Autowired
    private IAnyTxnTransactionService anyTxnTransactionService;

    @Autowired
    private ICmCustomerLimitFrozeServiceImpl iCmCustomerLimitFrozeService;

    @Autowired
    private IAviatorService aviatorService;

    @Autowired
    private IInterestTableService interestTableService;

    @Resource
    private ExtCmCustomerExtInfoMapper extCmCustomerExtInfoMapper;

    @Autowired
    private IUserApiService iUserApiService;

    /**
     * 授信
     *
     * @param creditApprovalReq 授信请求报文
     * @throws TxnException
     * @throws SegmentException
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void saveCreditApproval(CreditApprovalReq creditApprovalReq) throws Exception {
        logger.debug("开始授信请求，参数为:{}", creditApprovalReq);
        //获取授信信息列表creditInfoList
        List<CreditInfoReq> creditInfoList = creditApprovalReq.getCreditInfoList();
        //判断请求列表中产品ID和合同号是否有重复，时间的是失效时间与生效时间比较 获取产品集合
        this.provingProduct(creditInfoList);
        //根据渠道号，userId获取客户数据 并判断客户数据是否正常
        CustomerSecondLevelRes secondLevel = iCustomerService.getByUserIdAndChannel(creditApprovalReq.getUserId(), creditApprovalReq.getChannel(), creditApprovalReq.getTenantId());
        //根据客户id，机构id，租户id获取客户一级信息
        CustomerFirstLevelRes firstLevel = iCustomerService.getFirstLevelByOrgIdAndCusId(secondLevel.getCustomerId(), secondLevel.getOrganizationId(), creditApprovalReq.getTenantId());
        //幂等性校验
        int count = extCmCustomerLimitInfoMapper.selectCountBySeqNo(creditApprovalReq.getSeqNo(), secondLevel.getCustomerId(), creditApprovalReq.getTenantId());
        if (count > 0) {
            return;
        }
        //根据渠道号获取机构ID
        PrChannelTable channelTable = iChannelTableService.getChannelTableByChannelIdAndStatus(CustomerConstant.STATUS_NORMAL, creditApprovalReq.getChannel(), creditApprovalReq.getTenantId());
        //获取机构中的客户额度模版id
        String limitTemplateId = this.getLimitTemplateId(channelTable.getOrganizationId(), creditApprovalReq.getTenantId());
        //将基本属性放入对象
        CmCustomerLimitInfo baseInfo = this.getBaseCustomerLimitInfo(creditApprovalReq, limitTemplateId, secondLevel.getCustomerId(), channelTable.getOrganizationId());

        CreditApprovalBean bean = new CreditApprovalBean();
        //父节点操作集合
        List<CmCustomerLimitInfo> operationCustomerLimitList = new ArrayList<>();
        //获取渠道，租户id机构对应的节点映射集合
        List<PrCreditLimitMapping> prCreditLimitMappingList = this.getLimiMapping(baseInfo.getOrganizationId(), baseInfo.getChannel(), baseInfo.getTenantId());

        //将集合转成<productId,limitId>的map集合
        Map<String, String> limitMappingMap = prCreditLimitMappingList.stream().collect(Collectors.toMap(PrCreditLimitMapping::getProductId, PrCreditLimitMapping::getLimitId));

        List<PrCreditLimit> prCreditLimitList = this.getCreaditLimit(baseInfo.getOrganizationId(), baseInfo.getTemplateId(), baseInfo.getTenantId());
        //获取map<limitId ,节点对象>
        Map<String, PrCreditLimit> prCreditLimitMap = prCreditLimitList.stream().collect(Collectors.toMap(PrCreditLimit::getLimitId, Function.identity()));
        //获取客户已有额度集合
        List<CmCustomerLimitInfo> allCmCustomerLimitInfoList = iCmCustomerLimitInfoService.selectCusLimitListByCustId(baseInfo.getOrganizationId(), baseInfo.getCustomerId(), baseInfo.getTenantId());

        Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = allCmCustomerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

        // 联合贷授信List
        List<InvestorCreditBean> investorCreditBeans = new ArrayList<>();

        //循环 授信信息列表creditInfoList
        for (CreditInfoReq creditInfo : creditInfoList) {
            //子节点处理逻辑
            //根据产品id从节点映射结合中获取对应节点映射对象
            String limitId = limitMappingMap.get(creditInfo.getProductId());
            //验证额度节点是否存在
            this.checkLimitId(limitId, creditInfo.getProductId());
            //根据节点映射集合对象中的节点ID从（节点模版+节点）的数据集合中获取对应数据
            PrCreditLimit prCreditLimit = prCreditLimitMap.get(limitId);
            //验证叶节点在模版中是否存在
            this.checkLimitTemplate(prCreditLimit, creditInfo.getProductId());
            //根据额度类型校验请求参数
            this.checkLimitType(creditInfo,customerLimitInfoMap,prCreditLimit.getUseMode(), limitId);
            //将数据存入需要添加的客户额度实体对象中
            CmCustomerLimitInfo cmCustomerLimitInfo = this.addCmCustomerLimitInfo(baseInfo, creditInfo.getCreditLimit(), creditInfo.getCreditLimit(), prCreditLimit);
            allCmCustomerLimitInfoList.add(cmCustomerLimitInfo);
            customerLimitInfoMap = allCmCustomerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

            //根据模版对象 判断节点是否存在父节点
            String fatherLimitId = prCreditLimit.getFatherLimitId();
            //授信金额
            BigDecimal creditLimit = creditInfo.getCreditLimit();

            //父节点处理
            while (StringUtils.isNotBlank(fatherLimitId)) {
                //根据额度节点id获取节点属性
                PrCreditLimit fatherCreditLimit = prCreditLimitMap.get(fatherLimitId);

                //父节点改造，按照额度计算方式从新进行计算
                CmCustomerLimitInfo fatherLimitInfo = this.addFatherLimitInfo(baseInfo, fatherCreditLimit, allCmCustomerLimitInfoList, customerLimitInfoMap);
                operationCustomerLimitList.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
                if (!fatherLimitInfo.getOcFlag().equals(CustomerConstant.OC_FLAG_0)) {
                    fatherLimitInfo.setUpdateBy(creditApprovalReq.getUserId());
                    operationCustomerLimitList.add(fatherLimitInfo);
                }

                allCmCustomerLimitInfoList.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
                allCmCustomerLimitInfoList.add(fatherLimitInfo);

                customerLimitInfoMap = allCmCustomerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

                creditLimit = fatherLimitInfo.getLimitAmount();

                //重新修改 fatherLimitId 循环
                fatherLimitId = fatherCreditLimit.getFatherLimitId();
            }
            //叶节点添加当前额度生效日期--请求报文（额度生效日期 yyyy-MM-dd）
            cmCustomerLimitInfo.setLimitEffectiveDate(creditInfo.getEffectiveDate());
            //叶节点添加当前额度失效日期--请求报文（额度失效日期 yyyy-MM-dd）
            cmCustomerLimitInfo.setLimitExpiryDate(creditInfo.getExpiryDate());
            //叶节点添加合同号--请求报文（合同号）
            cmCustomerLimitInfo.setContractNumber(creditInfo.getContractNumber());
            //叶节点添加流水号
            cmCustomerLimitInfo.setSeqNo(baseInfo.getSeqNo());
            //添加自增主键
            cmCustomerLimitInfo.setId(iSegmentService.getId(Constant.CM_CUSTOMER_LIMIT_INFO));
            //将叶节点直接放到需要添加的bean中
            bean.addAddCmCustomerLimitInfos(cmCustomerLimitInfo);

            // 获取产品信息
            PrProductTable productTable = productTableService.findProByProIdAndOrgId(creditInfo.getProductId(), channelTable.getOrganizationId(), creditApprovalReq.getTenantId());
            // 如果是联合贷产品，则mq发送联合贷授信申请
            if (CustomerConstant.YES_FLAG.equals(productTable.getAllowJointFlag())) {
                investorCreditBeans.add(new InvestorCreditBean(creditApprovalReq.getTenantId(), cmCustomerLimitInfo.getCustomerId(), cmCustomerLimitInfo.getOrganizationId(), creditInfo.getProductId(),
                        firstLevel.getChineseName(), creditApprovalReq.getSeqNo(), cmCustomerLimitInfo.getUseMode(), creditInfo.getCreditLimit(), creditApprovalReq.getChannel(), creditInfo.getContractNumber()));
            }
        }
        //根据渠道信息判断是否需要记录营销记录
        if (CustomerConstant.YES_FLAG.equals(channelTable.getMarketingRecordFlag())) {
            CmCustomerFirstLevel firstLevelForMarket = BeanMapping.copy(firstLevel, CmCustomerFirstLevel.class);
            CmCustomerSecondLevel secondLevelForMarket = BeanMapping.copy(secondLevel, CmCustomerSecondLevel.class);
            TlMarketingRecord marketingRecord = this.buildTlMarketingRecord(firstLevelForMarket,
                    secondLevelForMarket, new ArrayList<>(), operationCustomerLimitList, CustomerConstant.MARKET_STEP_2, creditApprovalReq.getSeqNo());
            bean.setMarketingRecord(marketingRecord);
        }

        if (CollectionUtils.isNotEmpty(operationCustomerLimitList)) {
            //提取添加集合中客户已存在额度节点
            for (CmCustomerLimitInfo customerLimitInfo : operationCustomerLimitList) {
                if (null != customerLimitInfo.getId()) {
                    //将修改的额度放入bean的修改中
                    bean.addModifyCmCustomerLimitInfos(customerLimitInfo);
                } else {
                    //不存在 创建主键
                    customerLimitInfo.setId(iSegmentService.getId(Constant.CM_CUSTOMER_LIMIT_INFO));
                    //放入bean的添加集合
                    bean.addAddCmCustomerLimitInfos(customerLimitInfo);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(investorCreditBeans)) {
            //如果需要进行联合贷授信则mq发送联合贷授信申请
            this.sendInvestorCreditMessage(investorCreditBeans, bean);
        } else {
            //根据limitId 合并添加集合
            iCmCustomerLimitInfoService.registerCustomerLimitInfo(bean);
        }

    }

    /**
     * 额度调整
     *
     * @param creditAdjustReq 额度调整请求报文
     * @throws TxnException
     */
    @Override
    public void modifyCreditApproval(CreditAdjustReq creditAdjustReq) throws TxnException {
        logger.debug("额度调整开始，参数:{}", creditAdjustReq);
        //根据渠道号获取机构ID 判断渠道是否存在渠道不存在---抛出异常结束
        PrChannelTable channelTable = iChannelTableService.getChannelTableByChannelIdAndStatus(CustomerConstant.STATUS_NORMAL, creditAdjustReq.getChannel(), creditAdjustReq.getTenantId());

        //根据渠道号，userId获取客户数据 并判断客户数据是否正常
        CustomerSecondLevelRes secondLevel = iCustomerService.getByUserIdAndChannel(creditAdjustReq.getUserId(), creditAdjustReq.getChannel(), creditAdjustReq.getTenantId());
        //获取渠道，租户id机构对应的节点映射集合
        List<PrCreditLimitMapping> prCreditLimitMappingList = this.getLimiMapping(channelTable.getOrganizationId(), creditAdjustReq.getChannel(), creditAdjustReq.getTenantId());
        //将集合转成<limitId,productId>的map集合
        Map<String, String> limitMappingMap = prCreditLimitMappingList.stream().collect(Collectors.toMap(PrCreditLimitMapping::getProductId, PrCreditLimitMapping::getLimitId));
        //获取产品额度集合
        List<CmCustomerLimitInfo> cmCustomerLimitInfoList = iCmCustomerLimitInfoService.selectCusLimitListByCustId(channelTable.getOrganizationId(), secondLevel.getCustomerId(), creditAdjustReq.getTenantId());
        //获取map<limitId,List<CmCustomerLimitInfo>> 产品对应额度为非循环额度时 一个额度节点id可以对应多条数据
        Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = cmCustomerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

        //额度调整的产品map集合
        HashMap<String, String> productMap = Maps.newHashMap();
        //声明空的额度调整的对象集合
        List<CmCustomerLimitInfo> list = new ArrayList<>();
        //声明空的调额历史表的对象集合
        List<CmLimitAdjustLog> limitAdjustLogList = new ArrayList<>();
        //上级需要修改金额的集合
        Map<String, BigDecimal> limitMap = Maps.newHashMap();
        //循环请求中的额度调整信息列表
        for (CreditAdjustInfoReq creditAdjustInfoReq : creditAdjustReq.getCreditInfoList()) {
            String productId = creditAdjustInfoReq.getProductId();
            //修改时间和额度不能同时为空
            this.checkModifyValue(creditAdjustInfoReq);
            //根据产品id 获取对应额度节点id
            String limitId = limitMappingMap.get(productId);
            //验证额度节点是否存在
            this.checkLimitId(limitId, productId);
            //根据节点映射的节点id 验证客户额度表 并获取对应的客户额度信息
            List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(limitId);
            //验证节点对应的客户额度是否存在
            this.checkCustomerLimitInfo(cmCustomerLimitInfos, productId);
            //获取第一条
            CmCustomerLimitInfo cmCustomerLimitInfo = cmCustomerLimitInfos.get(0);
            //判断客户的节点类型
            if (CustomerConstant.USER_MODEL_0.equalsIgnoreCase(cmCustomerLimitInfo.getUseMode())) {
                //当客户节点类型为循环额度时 ，产品对应额度节点不能重复 默认数据库现在循环额度已有一条
                this.checkProduct(productMap, productId, CustomerConstant.CUSTOMER_TYPE_CREDITADJUST);
                //将产品id放入map
                productMap.put(productId, productId);
            } else {
                //当额度类型为非循环额度时 验证合同号 获取合同号对应额度
                cmCustomerLimitInfo = this.checkContractNumber(creditAdjustInfoReq.getContractNumber(), cmCustomerLimitInfos, productId, CustomerConstant.CUSTOMER_TYPE_CREDITADJUST);
            }
            //验证客户信息
            this.checkCmCustomerLimitStatus(cmCustomerLimitInfo, productId);
            //验证是否满足调整客户额度的条件
            this.checkModifyCondition(creditAdjustInfoReq, cmCustomerLimitInfo);
            //业务日期
            PrOrganizationTable org = organizationTableService.findByOrId(cmCustomerLimitInfo.getOrganizationId(), cmCustomerLimitInfo.getTenantId());
            Date businessDate = org.getNextProcessingDate();
            //获取叶节点的修改实体
            CmCustomerLimitInfo customerLimitInfo = this.getCreditAdjustInfoModal(creditAdjustInfoReq, channelTable.getOrganizationId(), cmCustomerLimitInfo, secondLevel.getCustomerId(), limitId,
                    creditAdjustReq.getChannel(), creditAdjustReq.getUserId(), businessDate);
            //创建叶节点额度变化历史，并放进集合

            limitAdjustLogList.add(this.buildCmLimitAdjustLog(creditAdjustReq.getSeqNo(), creditAdjustInfoReq, cmCustomerLimitInfo, creditAdjustReq.getUserId(), businessDate));
            //判断请求报文中的调整后额度是否存在
            if (null != creditAdjustInfoReq.getCreditLimit()) {
                //更新客户额度枝节点修改额度
                this.getModifyFatherLimit(cmCustomerLimitInfo, limitMap, creditAdjustInfoReq.getCreditLimit(), productId, customerLimitInfoMap);
            }
            //将对象放入集合中
            list.add(customerLimitInfo);
        }
        //判断需要修改的枝节点是否存在
        if (!limitMap.isEmpty()) {
            //存在 调用方法获取需要修改的枝节点的集合
            List<CmCustomerLimitInfo> lists = this.getLimitModifyList(limitMap, customerLimitInfoMap, channelTable.getOrganizationId(), secondLevel.getCustomerId(), creditAdjustReq.getUserId(),
                    creditAdjustReq.getChannel());
            //将集合放入已有集合中
            list.addAll(lists);
        }
        //调用修改额度方法
        iCmCustomerLimitInfoService.modifyCustomerLimitList(list,limitAdjustLogList);
    }

    /**
     * 额度冻结/解冻
     *
     * @param creditFreezeReq 额度冻结，解冻请求报文
     * @throws TxnException
     */
    @Override
    public void modifyCreditFreeze(CreditFreezeReq creditFreezeReq) throws TxnException {
        logger.debug("额度冻结/解冻操作开始");
        //根据渠道号获取机构ID 判断渠道是否存在渠道不存在---抛出异常结束
        PrChannelTable channelTable = iChannelTableService.getChannelTableByChannelIdAndStatus(CustomerConstant.STATUS_NORMAL, creditFreezeReq.getChannel(), creditFreezeReq.getTenantId());

        //根据渠道号，userId获取客户数据 并判断客户数据是否正常
        CustomerSecondLevelRes secondLevel = iCustomerService.getByUserIdAndChannel(creditFreezeReq.getUserId(), creditFreezeReq.getChannel(), creditFreezeReq.getTenantId());
        //获取渠道，租户id机构对应的节点映射集合
        List<PrCreditLimitMapping> prCreditLimitMappingList = this.getLimiMapping(channelTable.getOrganizationId(), creditFreezeReq.getChannel(), creditFreezeReq.getTenantId());

        //获取节点映射中的额度·节点集合
        List<String> limitList = prCreditLimitMappingList.stream().map(PrCreditLimitMapping::getLimitId).collect(Collectors.toList());
        //将集合转成<productId,limitId>的map集合
        Map<String, String> limitMappingMap = prCreditLimitMappingList.stream().collect(Collectors.toMap(PrCreditLimitMapping::getProductId, PrCreditLimitMapping::getLimitId));
        //获取客户额度集合
        List<CmCustomerLimitInfo> customerLimitInfoList = iCmCustomerLimitInfoService.getCusLimitByLimtIdAndCusIdAndOrgId(limitList, channelTable.getOrganizationId(), creditFreezeReq.getChannel(), secondLevel.getCustomerId(), creditFreezeReq.getTenantId(), null);
        //将客户额度转成map<limitId,List<CmCustomerLimitInfo>>
        Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = customerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));
        //获取业务日期
        PrOrganizationTable org = organizationTableService.findByOrId(channelTable.getOrganizationId(), creditFreezeReq.getTenantId());
        Date businessDate = org.getNextProcessingDate();

        List<CmCustomerLimitInfo> list = new ArrayList<>();
        //声明空的调额历史表的对象集合
        List<CmLimitAdjustLog> limitAdjustLogList = new ArrayList<>();
        HashMap<String, String> productMap = Maps.newHashMap();
        //循环额度冻结/解冻信息列表
        for (CreditFreeze creditFreeze : creditFreezeReq.getCreditInfoList()) {
            //验证节点是否存在
            String limitId = limitMappingMap.get(creditFreeze.getProductId());
            //判断对象是否存在
            this.checkLimitId(limitId, creditFreeze.getProductId());
            //客户额度是否存在
            List<CmCustomerLimitInfo> customerLimitInfoLists = customerLimitInfoMap.get(limitId);
            if (customerLimitInfoLists == null || customerLimitInfoLists.isEmpty()) {
                //节点不存在数据抛出异常
                String noError = String.format(ErrorMsgConstant.CREDIT_LIMIT_PRODUCT_ERROR, creditFreeze.getProductId());
                logger.error(noError);
                throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
            }
            CmCustomerLimitInfo customerLimits = customerLimitInfoLists.get(0);
            String contractNumber = creditFreeze.getContractNumber();
            //当客户额度不为循环额度时 合同号不能为空
            if (CustomerConstant.USER_MODEL_0.equalsIgnoreCase(customerLimits.getUseMode())) {
                String productId = creditFreeze.getProductId();
                //验证产品id是否重复
                this.checkProduct(productMap, productId, CustomerConstant.CUSTOMER_TYPE_CREDITFREEZE);
                //将产品id放入map
                productMap.put(productId, productId);
            } else {
                //验证对应额度是否存在该合同号的授信
                customerLimits = this.checkContractNumber(contractNumber, customerLimitInfoLists, creditFreeze.getProductId(), CustomerConstant.CUSTOMER_TYPE_CREDITFREEZE);
            }
            // 直接 验证冻结/解冻标志
            this.verifyOperateType(creditFreeze.getOperateType(), customerLimits.getLimitStatus(), creditFreeze.getProductId());
            //创建修改的对象
            CmCustomerLimitInfo customerLimitInfo = this.buildModifyCustomerLimit(customerLimits, creditFreeze, creditFreezeReq.getUserId());
            //
            limitAdjustLogList.add(this.createCmLimitAdjustLog(creditFreezeReq.getSeqNo(), creditFreeze, customerLimits, creditFreezeReq.getUserId(), businessDate, creditFreezeReq.getAdjustReason()));

            //将对象放入集合中
            list.add(customerLimitInfo);
        }
        //修改客户额度标志
        iCmCustomerLimitInfoService.modifyCustomerLimitStatus(list);
        //保存调额记录
        for (CmLimitAdjustLog cmLimitAdjustLog : limitAdjustLogList) {
            cmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
        }
    }

    /**
     * 客户额度树
     *
     * @param creditAllInfoQueryReq 客户额度树查询请求报文
     * @return
     * @throws TxnException
     */

    @Override
    public CreditAllInfoQueryRes getAllCreditApproval(CreditAllInfoQueryReq creditAllInfoQueryReq) throws TxnException {
        logger.debug("客户额度树查询开始");
        //获取请求报文中客户id
        String customerId = creditAllInfoQueryReq.getCustomerId();
        //获取请求报文中证件号
        String idNumber = creditAllInfoQueryReq.getIdNumber();
        //获取请求报文中证件类型
        String idType = creditAllInfoQueryReq.getIdType();
        //获取请求报文中机构号
        String organizationId = creditAllInfoQueryReq.getOrganizationId();
        //获取请求报文中租户id
        String tenantId = creditAllInfoQueryReq.getTenantId();
        //验证证件类型和证件号是否同时存在或不存在,验证客户id与证件是否存在一个查询条件
        this.verifyIdNumberAndType(idNumber, idType, customerId);
        //声明空的 客户一级对象
        CustomerFirstLevelRes customerFirstLevelRes;
        //判断客户id是否存在
        if (StringUtils.isNotBlank(customerId)) {
            //存在 根据客户id ，机构号，租户id，查询客户一级表获取数据
            customerFirstLevelRes = iCustomerService.getFirstLevelByOrgIdAndCusId(customerId, organizationId, tenantId);
            //判断报文中证件号码与库表中是否一致
            if (StringUtils.isNotBlank(idNumber) && !customerFirstLevelRes.getIdNumber().equalsIgnoreCase(idNumber)) {
                logger.error(ErrorMsgConstant.CUSTOMER_FIRST_ERROR_IDNUMBER);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            //判断报文中证件类型与库表中是否一致
            if (StringUtils.isNotBlank(idType) && !customerFirstLevelRes.getIdType().equalsIgnoreCase(idType)) {
                logger.error(ErrorMsgConstant.CUSTOMER_FIRST_ERROR_IDTYPE);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
        } else {
            //根据 证件号，证件类型，机构号租户id查询一级表信息
            customerFirstLevelRes = iCustomerService.getCustomerByOrgIdAndIdCard(idNumber, idType, organizationId, tenantId);
            //更新客户id
            customerId = customerFirstLevelRes.getCustomerId();
        }
        CreditAllInfoQueryRes creditAllInfoQueryRes = new CreditAllInfoQueryRes();
        //客户信息放入返回
        creditAllInfoQueryRes.setCustomerId(customerId);
        //请求报文流水号放入返回
        creditAllInfoQueryRes.setSeqNo(creditAllInfoQueryReq.getSeqNo());
        //中文名称放入返回
        creditAllInfoQueryRes.setChineseName(customerFirstLevelRes.getChineseName());
        //证件号放入返回
        creditAllInfoQueryRes.setIdNumber(customerFirstLevelRes.getIdNumber());
        //证件类型放入返回
        creditAllInfoQueryRes.setIdType(customerFirstLevelRes.getIdType());
        //根据客户id获取客户额度信息
        List<CreditInfoTree> infoTrees = iCmCustomerLimitInfoService.getCusLimitTree(null, customerId, CustomerConstant.CREDIT_LIMIT_STATUS_0, tenantId);
        //将查询数据放入返回集合中
        creditAllInfoQueryRes.setCreditInfoList(infoTrees);
        logger.debug("客户额度树查询结束，查询数据结果data为:{}", creditAllInfoQueryRes);
        return creditAllInfoQueryRes;
    }

    /**
     * 按渠道查询客户额度
     *
     * @param creditInfoQueryReq 客户额度查询请求报文
     * @return
     * @throws TxnException
     */
    @Override
    public CreditInfoQueryRes getCreditApproval(CreditInfoQueryReq creditInfoQueryReq) throws TxnException {
        logger.debug("按渠道查询客户额度开始，参数:{}", creditInfoQueryReq);

        CreditInfoQueryRes creditInfoQueryRes = new CreditInfoQueryRes();
        //将请求报文中需要返回值放入返回对象中
        creditInfoQueryRes.setUserId(creditInfoQueryReq.getUserId());
        //将流水号放入返回对象中
        creditInfoQueryRes.setSeqNo(creditInfoQueryReq.getSeqNo());
        //根据渠道号获取机构ID 判断渠道是否存在渠道不存在---抛出异常结束
        PrChannelTable channelTable = iChannelTableService.getChannelTableByChannelIdAndStatus(CustomerConstant.STATUS_NORMAL, creditInfoQueryReq.getChannel(), creditInfoQueryReq.getTenantId());
        //根据渠道号，userId获取客户数据 并判断客户数据是否正常
        CustomerSecondLevelRes secondLevel = iCustomerService.getByUserIdAndChannel(creditInfoQueryReq.getUserId(), creditInfoQueryReq.getChannel(), creditInfoQueryReq.getTenantId());
        if (StringUtils.isBlank(creditInfoQueryReq.getProductId())) {//按照渠道
            //获取渠道，租户id机构对应的节点映射集合
            List<PrCreditLimitMapping> prCreditLimitMappingList = iCreditLimitMappingService.getLimitMappingByChannelAndOrgId(channelTable.getOrganizationId(), creditInfoQueryReq.getChannel(), creditInfoQueryReq.getTenantId());
            //判断是否存在节点
            if (prCreditLimitMappingList.isEmpty()) {
                logger.error(ErrorMsgConstant.CREDIT_LIMITMAPPING_NODATA);
                throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
            }

            //获取客户已有额度集合 可用额度会按照父节点计算
            List<CmCustomerLimitInfo> customerLimitInfoList = iCmCustomerLimitInfoService.selectCusLimitListByCustId(secondLevel.getOrganizationId(), secondLevel.getCustomerId(), creditInfoQueryReq.getTenantId());

            Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = customerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

            //获取返回集合
            List<CreditInfoRes> creditInfoList = new ArrayList<>();
            for (PrCreditLimitMapping creditLimitMapping : prCreditLimitMappingList) {
                //创建返回对象
                List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(creditLimitMapping.getLimitId());
                if (CollectionUtils.isNotEmpty(cmCustomerLimitInfos)) {
                    for (CmCustomerLimitInfo cmCustomerLimitInfo : cmCustomerLimitInfos) {
                        if (cmCustomerLimitInfo != null) {
                            PrProductTable productTable=new PrProductTable();
                            productTable.setProductId(creditLimitMapping.getProductId());
                            CreditInfoRes creditInfoRes = this.buildCreditInfo(cmCustomerLimitInfo,customerLimitInfoMap,productTable);
                            //产品id放入返回集合中
                            creditInfoRes.setProductId(creditLimitMapping.getProductId());
                            //产品类型放入返回集合
                            PrProductTable product = productTableService.findProByProIdAndOrgId(creditLimitMapping.getProductId(),
                                    channelTable.getOrganizationId(), creditInfoQueryReq.getTenantId());
                            if(null != product){
                                creditInfoRes.setProductType(product.getTableType());
                            }
                            // 查询客户对应客群年利率
                            creditInfoRes.setYearRate(this.getInterest(secondLevel, product));
                            creditInfoList.add(creditInfoRes);
                        }
                    }
                }
            }
            creditInfoQueryRes.setCreditInfoList(creditInfoList);
        } else {
            PrProductTable productTable;
            try {
                productTable = productTableService.findProByProIdAndOrgId(creditInfoQueryReq.getProductId(),
                        channelTable.getOrganizationId(), creditInfoQueryReq.getTenantId());
            } catch (TxnBizException e) {
                String message = MessageFormat.format("产品不存在! 产品号product_id:{0},机构号organizationId：{1}，租户号tenantId:{2}", creditInfoQueryReq.getProductId(),
                        channelTable.getOrganizationId(), creditInfoQueryReq.getTenantId());
                logger.error(message);
                throw new TxnRuntimeException(message);
            }
            final PrProductTable product = productTable;
            //根据产品id查询节点映射表 查询额度id
            CreditLimitMappingRes limitMapping = iCreditLimitMappingService.findByTenIdAndChannelAndProId(creditInfoQueryReq.getTenantId(), creditInfoQueryReq.getChannel(), creditInfoQueryReq.getProductId());
            if (limitMapping == null || StringUtils.isBlank(limitMapping.getLimitId())) {
                //产品对应节点不存在
                String productError = String.format(ErrorMsgConstant.CUSTOMER_QUERY_PRODCT_NODATA, creditInfoQueryReq.getProductId());
                logger.error(productError);
                throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
            }
            //获取客户额度集合
            List<CmCustomerLimitInfo> customerLimitInfoList = iCmCustomerLimitInfoService.selectCusLimitListByCustId(secondLevel.getOrganizationId(), secondLevel.getCustomerId(), creditInfoQueryReq.getTenantId());

            Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = customerLimitInfoList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

            //过滤出产品Id对应limitId的客户额度信息列表
            List<CmCustomerLimitInfo> customerProductLimitInfoList = customerLimitInfoList.stream().
                    filter(cmCustomerLimitInfo -> cmCustomerLimitInfo.getLimitId().equals(limitMapping.getLimitId())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(customerProductLimitInfoList)) {
                String message = MessageFormat.format("userId:{0},客户号：{1},产品号：{2},limitId：{3},额度信息不存在",
                        creditInfoQueryReq.getUserId(), secondLevel.getCustomerId(), creditInfoQueryReq.getProductId(), limitMapping.getLimitId());
                logger.error(message);
                throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), message);
            }
            List<CreditInfoRes> creditInfoList = new ArrayList<>();
            customerProductLimitInfoList.forEach(limitInfo -> {
                CreditInfoRes creditInfoRes = this.buildCreditInfo(limitInfo, customerLimitInfoMap, product);
                creditInfoList.add(creditInfoRes);
            });
            creditInfoQueryRes.setCreditInfoList(creditInfoList);
        }
        logger.debug("按渠道查询客户额度结束，查询数据结果data为:{}", creditInfoQueryRes);
        return creditInfoQueryRes;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CmCustomerLimitInfo batchLimitAdjust(BtLimitAdjustInfo btLimitAdjustInfo) throws TxnException {
        //验证机构是否存在
        PrOrganizationTable organizationTable = iOrganizationTableService.findByOrId(btLimitAdjustInfo.getOrganizationId(), btLimitAdjustInfo.getTenantId());
        //验证不通过记入调额历史表
        if(null == organizationTable){
            insertCmLimitAdjustLog(btLimitAdjustInfo,"未获取到机构信息",organizationTable.getNextProcessingDate());
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), TxnRespCode.BIZ_ERROR.getUserTips());
        }

        Date businessDate = organizationTable.getNextProcessingDate();

        //验证模板id是否与机构对应
        if (StringUtils.isNotBlank(btLimitAdjustInfo.getTemplateId()) && !btLimitAdjustInfo.getTemplateId().equals(organizationTable.getCreditLimitTemplateId())) {
            insertCmLimitAdjustLog(btLimitAdjustInfo,ErrorMsgConstant.CREDIT_ORG_NOLIMITID,businessDate);

            logger.error(ErrorMsgConstant.CREDIT_ORG_NOLIMITID);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), TxnRespCode.BIZ_ERROR.getUserTips());
        }

        //验证渠道是否存在
        PrChannelTable channelTable = iChannelTableService.getChannelTableByChannelIdAndStatus(CustomerConstant.STATUS_NORMAL, btLimitAdjustInfo.getChannel(), btLimitAdjustInfo.getTenantId());
        if(null == channelTable){
            insertCmLimitAdjustLog(btLimitAdjustInfo,"渠道不存在",businessDate);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), TxnRespCode.BIZ_ERROR.getUserTips());
        }

        //验证客户是否存在
        CmCustomerSecondLevel customerSecondLevel = iCustomerService.getSecondByChannelAndCustomerIdAndTensnId(channelTable.getChannelId(), btLimitAdjustInfo.getCustomerId(), btLimitAdjustInfo.getTenantId(), null);
        if(customerSecondLevel == null){
            insertCmLimitAdjustLog(btLimitAdjustInfo,"客户不存在",businessDate);
            logger.error("客户不存在");
            throw new TxnBizException("客户不存在");
        }

        //查询额度信息并校验
        CmCustomerLimitInfo customerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(btLimitAdjustInfo.getCustomerId(), organizationTable.getOrganizationId(), btLimitAdjustInfo.getLimitId(), null, btLimitAdjustInfo.getTenantId());

        if (customerLimitInfo == null) {
            insertCmLimitAdjustLog(btLimitAdjustInfo,ErrorMsgConstant.CUSTOMER_LIMIT_NOT_EXIST,businessDate);
            logger.error(ErrorMsgConstant.CUSTOMER_LIMIT_NOT_EXIST);
            throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_NOT_EXIST);
        }
        logger.info("客户额度信息:id = {}, custId = {}" ,customerLimitInfo.getId(),customerLimitInfo.getCustomerId());

        //验证额度是否是循环额度
        if (!CustomerConstant.USER_MODEL_0.equals(customerLimitInfo.getUseMode())) {
            insertCmLimitAdjustLog(btLimitAdjustInfo,ErrorMsgConstant.LIMIT_USER_MODE_1,businessDate);
            logger.error(ErrorMsgConstant.LIMIT_USER_MODE_1);
            throw new TxnBizException(ErrorMsgConstant.LIMIT_USER_MODE_1);
        }

        //校验额度值是否满足调整要修改的额度值
        if (customerLimitInfo.getLimitUsed().compareTo(btLimitAdjustInfo.getLimitPerm()) > 0) {
            insertCmLimitAdjustLog(btLimitAdjustInfo,ErrorMsgConstant.CUSTOMER_LIMIT_USED_GREATER_ADJUST,businessDate);
            logger.error(ErrorMsgConstant.CUSTOMER_LIMIT_USED_GREATER_ADJUST);
            throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_USED_GREATER_ADJUST);
        }

        //验证额度是否已经失效
        if (organizationTable.getNextProcessingDate().compareTo(customerLimitInfo.getLimitExpiryDate()) > 0) {
            insertCmLimitAdjustLog(btLimitAdjustInfo,ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_0,businessDate);
            logger.error(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_0);
            throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_0);
        }

        List<CmCustomerLimitInfo> updateList = new ArrayList<>();//待处理额度信息

        //构建原始额度信息 方便存储日志
        CmCustomerLimitInfo orgCmCustomerLimitInfo = BeanMapping.copy(customerLimitInfo, CmCustomerLimitInfo.class);
        //获取要更新子额度节点信息
        customerLimitInfo.setLimitPerm(btLimitAdjustInfo.getLimitPerm());
        customerLimitInfo.setLimitAmount(btLimitAdjustInfo.getLimitPerm());
        customerLimitInfo.setLimitAvailable(customerLimitInfo.getLimitAmount().subtract(customerLimitInfo.getLimitUsed()));
        customerLimitInfo.setUpdateBy(btLimitAdjustInfo.getUpdateBy());
        updateList.add(customerLimitInfo);

        List<PrCreditLimit> prCreditLimitList = this.getCreaditLimit(customerLimitInfo.getOrganizationId(), customerLimitInfo.getTemplateId(), customerLimitInfo.getTenantId());
        //获取map<limitId ,节点对象>
        Map<String, PrCreditLimit> prCreditLimitMap = prCreditLimitList.stream().collect(Collectors.toMap(PrCreditLimit::getLimitId, Function.identity()));

        //根据客户查询客户所有额度信息，父节点需要按照子节点调整
        //获取客户已有额度集合
        List<CmCustomerLimitInfo> allCustLimits = iCmCustomerLimitInfoService.selectCusLimitListByCustId(customerLimitInfo.getOrganizationId(), customerLimitInfo.getCustomerId(), customerLimitInfo.getTenantId());
        //删除原调额子节点信息，将最新的子节点信息放到集合中
        allCustLimits.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(customerLimitInfo.getLimitId()));
        allCustLimits.add(customerLimitInfo);

        Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = allCustLimits.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

        //根据模版对象 判断节点是否存在父节点
        String fatherLimitId = customerLimitInfo.getFatherId();

        //循环处理父节点
        while (StringUtils.isNotBlank(fatherLimitId)) {

            PrCreditLimit prCreditLimit = prCreditLimitMap.get(fatherLimitId);//父节点
            //原父节点额度信息
            List<CmCustomerLimitInfo> fatherlist = customerLimitInfoMap.get(prCreditLimit.getLimitId());
            //校验父节点信息
            this.checkCmCustomerLimitStatus(fatherlist.get(0), "");

            //父节点改造，按照额度计算方式从新进行计算
            CmCustomerLimitInfo fatherLimitInfo = this.addFatherLimitInfo(customerLimitInfo, prCreditLimit, allCustLimits,customerLimitInfoMap);
            fatherLimitInfo.setUpdateBy(btLimitAdjustInfo.getUpdateBy());
            updateList.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
            if(!fatherLimitInfo.getOcFlag().equals(CustomerConstant.OC_FLAG_0)){
                updateList.add(fatherLimitInfo);
            }

            allCustLimits.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
            allCustLimits.add(fatherLimitInfo);

            customerLimitInfoMap = allCustLimits.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

            //重新修改 fatherLimitId 循环
            fatherLimitId = prCreditLimit.getFatherLimitId();
        }
        logger.info("uodateList条数 :{}",updateList.size());

        //调用修改额度方法
        if (CollectionUtils.isNotEmpty(updateList)) {
            for (CmCustomerLimitInfo cmCustomerLimitInfo : updateList) {
                int i = extCmCustomerLimitInfoMapper.updateCustomerLimitAdjust(cmCustomerLimitInfo);
                if (i <= 0) {
                    logger.error(ErrorMsgConstant.CREDIT_LIMIT_MODIFY_ERROR);
                    throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
                }
            }
        }
        return orgCmCustomerLimitInfo;
    }

    /**
     * 验证叶节点在额度模版中是否存在
     *
     * @param prCreditLimit
     */
    private void checkLimitTemplate(PrCreditLimit prCreditLimit, String product) throws TxnBizException {
        if (prCreditLimit == null) {
            String limitTempError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_LIMIT_NOTEMPLATE, product);
            throw new TxnBizException(limitTempError);
        }
    }

    /**
     * 根据额度类型校验请求参数
     * @param creditInfo
     * @param customerLimitInfoMap
     * @param useMode
     * @param limitId
     * @throws TxnBizException
     */
    private void checkLimitType(CreditInfoReq creditInfo,Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap,String useMode, String limitId) throws TxnBizException {
        String productId = creditInfo.getProductId();
        if (CustomerConstant.USER_MODEL_0.equalsIgnoreCase(useMode)) {
            //判断客户额度表是否已使用
            List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(limitId);
            if (cmCustomerLimitInfos != null && !cmCustomerLimitInfos.isEmpty()) {
                //如果客户已使用产品对应节点 抛出异常
                String productError = String.format(ErrorMsgConstant.CREDIT_LIMIT_PRODUCT_USE, productId);
                throw new TxnBizException(productError);
            }
        }else if(CustomerConstant.USER_MODEL_1.equalsIgnoreCase(useMode) || CustomerConstant.USER_MODEL_2.equalsIgnoreCase(useMode)){
            if(StringUtils.isBlank(creditInfo.getContractNumber())){
                throw new TxnBizException(TxnRespCode.PARAM_ERROR.getMsg());
            }
        }
    }

    /**
     * 验证额度节点是否存在
     *
     * @param limitId
     * @param productId
     * @throws TxnException
     */
    private void checkLimitId(String limitId, String productId) throws TxnException {
        if (StringUtils.isBlank(limitId)) {
            //对象不存在 抛出异常结束
            String noError = String.format(ErrorMsgConstant.CREDIT_LIMIT_MAPPING_NODATA, productId);
            logger.error(noError);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
    }

    /**
     * 创建修改的客户额度实体
     *
     * @param cmCustomerLimit
     * @param baseInfo
     * @param limit
     * @return
     */
    private CmCustomerLimitInfo getCmCustomerLimitInfo(CmCustomerLimitInfo cmCustomerLimit, CmCustomerLimitInfo baseInfo, BigDecimal limit) {
        //声明新的实体
        CmCustomerLimitInfo cmCustomerLimitInfo = new CmCustomerLimitInfo();
        //数据的主键ID
        cmCustomerLimitInfo.setId(cmCustomerLimit.getId());
        //额度的节点ID
        cmCustomerLimitInfo.setLimitId(cmCustomerLimit.getLimitId());
        //额度所属机构号
        cmCustomerLimitInfo.setOrganizationId(cmCustomerLimit.getOrganizationId());
        //额度所属客户
        cmCustomerLimitInfo.setCustomerId(cmCustomerLimit.getCustomerId());
        //租户id
        cmCustomerLimitInfo.setTenantId(cmCustomerLimit.getTenantId());
        //客户修改后额度
        cmCustomerLimitInfo.setLimitAmount(cmCustomerLimit.getLimitAmount().add(limit));
        //客户修改后可使用额度
        cmCustomerLimitInfo.setLimitAvailable(cmCustomerLimit.getLimitAvailable().add(limit));
        //修改人--请求时的userId
        cmCustomerLimitInfo.setUpdateBy(baseInfo.getUpdateBy());
        //修改的渠道
        cmCustomerLimitInfo.setChannel(baseInfo.getChannel());
        //版本号
        cmCustomerLimitInfo.setRecordVersionNumber(cmCustomerLimit.getRecordVersionNumber());
        return cmCustomerLimitInfo;
    }

    /**
     * 根据机构号 渠道 租户id 获取额度相关信息
     *
     * @param organizationId
     * @param limitTemplateId
     * @param tenantId
     * @return
     * @throws TxnException
     */
    private List<PrCreditLimit> getCreaditLimit(String organizationId, String limitTemplateId, String tenantId) throws TxnException {
        //根据节点模版ID,机构号关联查询需要的节点信息
        List<PrCreditLimit> prCreditLimitList = iCreditLimitTemplateService.getPrCreditLimitListByTemIdAndOrgId(organizationId, limitTemplateId, tenantId);
        if (prCreditLimitList.isEmpty()) {
            logger.error(ErrorMsgConstant.CREDIT_LIMITTEMPLPATE_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA,TxnRespCode.NO_DATA.getUserTips());
        }
        return prCreditLimitList;
    }

    /**
     * 根据机构号，渠道，租户id获取正确节点映射集合
     *
     * @param organizationId
     * @param channel
     * @param tenantId
     * @return
     * @throws TxnException
     */
    private List<PrCreditLimitMapping> getLimiMapping(String organizationId, String channel, String tenantId) throws TxnException {
        List<PrCreditLimitMapping> prCreditLimitMappingList = iCreditLimitMappingService.getLimitMappingByChannelAndOrgId(organizationId, channel, tenantId);
        //判断是否存在节点
        if (prCreditLimitMappingList == null || prCreditLimitMappingList.isEmpty()) {
            logger.error(ErrorMsgConstant.CREDIT_LIMITMAPPING_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        return prCreditLimitMappingList;
    }

    /**
     * 获取额度模版id
     *
     * @param organizationId
     * @param tenantId
     * @return
     */
    private String getLimitTemplateId(String organizationId, String tenantId) throws TxnException {
        //根据机构号获去正常状态机构数据，不存在抛异常
        PrOrganizationTable prOrganizationTable = iOrganizationTableService.findByOrId(organizationId, tenantId);

        //判断该机构是否存在额度模版id
        if (StringUtils.isBlank(prOrganizationTable.getCreditLimitTemplateId())) {
            //机构中不存在额度模板---抛出异常结束
            logger.error(ErrorMsgConstant.CREDIT_ORG_NOLIMITID);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        return prOrganizationTable.getCreditLimitTemplateId();
    }

    /**
     * 获取基本属性组成对象
     *
     * @param creditApprovalReq
     * @param limitTemplateId
     * @param customerId
     * @param organizationId
     * @return CmCustomerLimitInfo
     */
    private CmCustomerLimitInfo getBaseCustomerLimitInfo(CreditApprovalReq creditApprovalReq, String limitTemplateId, String customerId, String organizationId) {
        //声明添加对象，放入基础值
        CmCustomerLimitInfo info = new CmCustomerLimitInfo();
        //客户ID---客户表
        info.setCustomerId(customerId);
        //机构号--机构表
        info.setOrganizationId(organizationId);
        //渠道号--授信请求报文(渠道号)
        info.setChannel(creditApprovalReq.getChannel());
        //将报告中的userId放入修改人中
        info.setUpdateBy(creditApprovalReq.getUserId());
        //租户id
        info.setTenantId(creditApprovalReq.getTenantId());
        //流水号
        info.setSeqNo(creditApprovalReq.getSeqNo());
        //额度模版id
        info.setTemplateId(limitTemplateId);

        return info;
    }


    private Map<String, BigDecimal> getModifyFatherLimit(CmCustomerLimitInfo cmCustomerLimitInfo, Map<String, BigDecimal> limitMap, BigDecimal creditLimit, String productId, Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap) throws TxnException {
        String fatherId = cmCustomerLimitInfo.getFatherId();
        while (StringUtils.isNotBlank(fatherId)) {
            //判断需要修改额度map中是否已存在该节点
            BigDecimal fatherIdLimit = limitMap.get(fatherId);
            //父节点存在，获取当前叶节点的额度变化量
            BigDecimal subtract = creditLimit.subtract(cmCustomerLimitInfo.getLimitAmount());
            //判断额度父节点是否存在
            if (fatherIdLimit != null) {
                //不存再 将新的变化值放入集合中
                subtract = subtract.add(fatherIdLimit);
            }
            //将变化量放入集合
            limitMap.put(fatherId, subtract);
            //重置fatherId 循环
            List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(fatherId);
            CmCustomerLimitInfo customerLimitInfo = cmCustomerLimitInfos.get(0);
            //验证父级客户信息
            this.checkCmCustomerLimitStatus(customerLimitInfo, productId);
            fatherId = customerLimitInfo.getFatherId();
        }
        return limitMap;
    }


    private CmCustomerLimitInfo getCreditAdjustInfoModal(CreditAdjustInfoReq creditAdjustInfoReq, String organizationId, CmCustomerLimitInfo cmCustomerLimitInfo, String customerId,
                                                         String limitId, String channel, String userId, Date businessDate) {

        //声明空的客户额度
        CmCustomerLimitInfo customerLimitInfo = new CmCustomerLimitInfo();
        //将主键，渠道号，节点id，客户id，机构号赋值给新的客户额度req实体
        customerLimitInfo.setId(cmCustomerLimitInfo.getId());
        customerLimitInfo.setTenantId(cmCustomerLimitInfo.getTenantId());
        customerLimitInfo.setOrganizationId(organizationId);
        customerLimitInfo.setLimitId(limitId);
        customerLimitInfo.setCustomerId(customerId);
        customerLimitInfo.setChannel(channel);
        //判断请求报文中的调整后额度是否存在
        BigDecimal creditLimit = creditAdjustInfoReq.getCreditLimit();
        if (null != creditLimit) {
            //调整后额度存在
            //将调整后额度放在当前生效额度字段中
            customerLimitInfo.setLimitAmount(creditLimit);
            //将调整后额度放在永久额度字段中
            customerLimitInfo.setLimitPerm(creditLimit);
            //获取额度调整后叶节点的可用额度
            BigDecimal remainLimitAvailable = creditLimit.subtract(cmCustomerLimitInfo.getLimitUsed()).subtract(cmCustomerLimitInfo.getOnlinePaymentAmount());
            BigDecimal limitAvailable = remainLimitAvailable.compareTo(BigDecimal.ZERO) > 0 ? remainLimitAvailable : BigDecimal.ZERO;
            //将新的可用额度放在临时额度中
            customerLimitInfo.setLimitAvailable(limitAvailable);
        }
        // 将报文中的授信合同号放入授信合同号字段中
        customerLimitInfo.setContractNumber(creditAdjustInfoReq.getContractNumber());
        //判断请求报文中调整后额度生效日期是否存在
        if (null != creditAdjustInfoReq.getEffectiveDate()) {
            //存在，将报文中的生效日期放入生效日期字段中
            customerLimitInfo.setLimitEffectiveDate(creditAdjustInfoReq.getEffectiveDate());
            //将原有生效日期放入临时生效日期中
            customerLimitInfo.setLimitTempStartDate(cmCustomerLimitInfo.getLimitEffectiveDate());
        }
        //判断请求报文中调整后额度失效日期是否存在
        if (null != creditAdjustInfoReq.getExpiryDate()) {
            //存在，将报文中的失效日期放入失效日期字段中
            customerLimitInfo.setLimitExpiryDate(creditAdjustInfoReq.getExpiryDate());
            //将原有失效日期放入临时失效日期中
            customerLimitInfo.setLimitTempEndDate(cmCustomerLimitInfo.getLimitExpiryDate());
        }
        customerLimitInfo.setLimitStatus(CustomerConstant.CREDIT_LIMIT_STATUS_1);
        //将报文中userId放入修改人中
        customerLimitInfo.setUpdateBy(userId);
        customerLimitInfo.setRecordVersionNumber(cmCustomerLimitInfo.getRecordVersionNumber());
        return customerLimitInfo;
    }

    /**
     * 调额历史实体
     *
     * @param seq
     * @param creditAdjustInfoReq
     * @param cmCustomerLimitInfo
     * @param userId
     * @return
     */
    private CmLimitAdjustLog buildCmLimitAdjustLog(String seq, CreditAdjustInfoReq creditAdjustInfoReq, CmCustomerLimitInfo cmCustomerLimitInfo, String userId, Date businessDate) {
        CmLimitAdjustLog cmLimitAdjustLog = new CmLimitAdjustLog();
        //主键
        try {
            cmLimitAdjustLog.setId(iSegmentService.getId(Constant.CM_LIMIT_ADJUST_LOG));
        } catch (SegmentException e) {
            logger.error("主键生成错误 ");
        }
        //租户ID
        cmLimitAdjustLog.setTenantId(cmCustomerLimitInfo.getTenantId());
        // 流水号
        cmLimitAdjustLog.setSeqNo(seq);
        //客户id
        cmLimitAdjustLog.setCustomerId(cmCustomerLimitInfo.getCustomerId());
        //业务机构id
        cmLimitAdjustLog.setOrganizationId(cmCustomerLimitInfo.getOrganizationId());
        //额度编号
        cmLimitAdjustLog.setLimitId(cmCustomerLimitInfo.getLimitId());
        //额度模板id
        cmLimitAdjustLog.setTemplateId(cmCustomerLimitInfo.getTemplateId());
        //调整额度不为空是
        if (creditAdjustInfoReq.getCreditLimit() != null) {
            //调额前永久额度
            cmLimitAdjustLog.setLimitPermBefore(cmCustomerLimitInfo.getLimitPerm());
            //调额后永久额度
            cmLimitAdjustLog.setLimitPermAfter(creditAdjustInfoReq.getCreditLimit());
        }
        //额度失效时间不为空时
        if (creditAdjustInfoReq.getExpiryDate() != null) {
            //调整前临时额度失效日期
            cmLimitAdjustLog.setLimitTempEndDateBefore(cmCustomerLimitInfo.getLimitExpiryDate());
            //调整后临时额度失效日期
            cmLimitAdjustLog.setLimitTempEndDateAfter(creditAdjustInfoReq.getExpiryDate());
        }
        //额度生效时间不为空时
        if (creditAdjustInfoReq.getEffectiveDate() != null) {
            // 调整前临时额度生效日期
            cmLimitAdjustLog.setLimitTempStartDateBefore(cmCustomerLimitInfo.getLimitEffectiveDate());
            //调整后临时额度生效日期
            cmLimitAdjustLog.setLimitTempStartDateAfter(creditAdjustInfoReq.getEffectiveDate());
        }
        //渠道号
        cmLimitAdjustLog.setChannel(cmCustomerLimitInfo.getChannel());
        //来源 	1.批量调额 2.联机调额
        cmLimitAdjustLog.setDataSource(CustomerConstant.DATASOURCE_2);
        //处理结果 S = 成功 F = 失败CustomerConstant.SUCCESS
        cmLimitAdjustLog.setResult(CustomerConstant.SUCCESS);
        //结果描述
        cmLimitAdjustLog.setDescription("调额");
        //额度调整类型 0 调额
        cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_0);//调额
        //最后更新操作员
        cmLimitAdjustLog.setUpdateBy(userId);
        //业务日期
        cmLimitAdjustLog.setBusinessDate(businessDate);
        //记录版本号
        cmLimitAdjustLog.setRecordVersionNumber(Constant.VERSION_NUMBER);
        return cmLimitAdjustLog;
    }


    /**
     * 调额历史实体
     *
     * @param creditAdjustInfoReq
     * @param cmCustomerLimitInfo
     * @param userId
     * @return
     */
    private CmLimitAdjustLog buildCmLimitAdjustLogOfCms(CreditFreezeOfCmsReq creditAdjustInfoReq, CmCustomerLimitInfo cmCustomerLimitInfo, String userId, Date businessDate) {
        CmLimitAdjustLog cmLimitAdjustLog = new CmLimitAdjustLog();
        //主键
        try {
            cmLimitAdjustLog.setId(iSegmentService.getId(Constant.CM_LIMIT_ADJUST_LOG));
        } catch (SegmentException e) {
            logger.error("主键生成错误 ");
        }
        //租户ID
        cmLimitAdjustLog.setTenantId(cmCustomerLimitInfo.getTenantId());
        // 流水号
        cmLimitAdjustLog.setSeqNo(SeqUtils.getSNByLength(32));
        //客户id
        cmLimitAdjustLog.setCustomerId(cmCustomerLimitInfo.getCustomerId());
        //业务机构id
        cmLimitAdjustLog.setOrganizationId(cmCustomerLimitInfo.getOrganizationId());
        //额度编号
        cmLimitAdjustLog.setLimitId(cmCustomerLimitInfo.getLimitId());
        //额度模板id
        cmLimitAdjustLog.setTemplateId(cmCustomerLimitInfo.getTemplateId());
        //调整类型
        if ("F".equals(creditAdjustInfoReq.getFlag())) {
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_1);
            cmLimitAdjustLog.setDescription("冻结成功");
        } else if ("R".equals(creditAdjustInfoReq.getFlag())) {
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_2);
            cmLimitAdjustLog.setDescription("解冻成功");
        }  else if ("C".equals(creditAdjustInfoReq.getFlag())) {
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_4);
            cmLimitAdjustLog.setDescription("注销成功");
        } else {
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_0);
            cmLimitAdjustLog.setDescription("调额成功");
        }
        //调额原因
        cmLimitAdjustLog.setAdjustReason(creditAdjustInfoReq.getAdjustReason()==null?"":creditAdjustInfoReq.getAdjustReason());
        //调整额度不为空是
        if (creditAdjustInfoReq.getLimitAmount() != null) {
            //调额前永久额度
            cmLimitAdjustLog.setLimitPermBefore(cmCustomerLimitInfo.getLimitPerm());
            //调额后永久额度
            cmLimitAdjustLog.setLimitPermAfter(new BigDecimal(creditAdjustInfoReq.getLimitAmount()));
        }

        //渠道号
        cmLimitAdjustLog.setChannel(cmCustomerLimitInfo.getChannel());
        //来源 	1.批量调额 2.联机调额
        cmLimitAdjustLog.setDataSource(CustomerConstant.DATASOURCE_2);
        //处理结果 S = 成功 F = 失败CustomerConstant.SUCCESS
        cmLimitAdjustLog.setResult(CustomerConstant.SUCCESS);
        //结果描述
        //最后更新操作员
        cmLimitAdjustLog.setUpdateBy(userId);
        //记录版本号
        cmLimitAdjustLog.setRecordVersionNumber(Constant.VERSION_NUMBER);
        cmLimitAdjustLog.setBusinessDate(businessDate);
        return cmLimitAdjustLog;
    }


    /**
     * 验证是否满足调整客户额度的条件
     *
     * @param creditAdjustInfoReq
     * @param cmCustomerLimitInfo
     * @throws TxnException
     */
    private void checkModifyCondition(CreditAdjustInfoReq creditAdjustInfoReq, CmCustomerLimitInfo cmCustomerLimitInfo) throws TxnException {
        //若调整后额度生效日期不为空，则需要客户额度记录中的已使用额度=0 and 实时还款未入账金额=0 and 可用额度=当前生效额度；
        //if (null != creditAdjustInfoReq.getEffectiveDate() && this.compareLimit(cmCustomerLimitInfo)) {
        //    //请求报文中生效日期不为空 当客户额度未处于初始状态----抛出异常
        //    String limitError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_LIMIT_USE, creditAdjustInfoReq.getProductId());
        //    logger.error(limitError);
        //    throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        //}
        this.checkDate(creditAdjustInfoReq, cmCustomerLimitInfo);

        //调整后额度须大于等于（已使用额度与实时还款未入账金额）的和
        //if (null != creditAdjustInfoReq.getCreditLimit()) {
        //    BigDecimal limitUse = cmCustomerLimitInfo.getLimitUsed().add(cmCustomerLimitInfo.getOnlinePaymentAmount());
        //    if (creditAdjustInfoReq.getCreditLimit().compareTo(limitUse) < 1) {
        //        String beError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_LIMIT, creditAdjustInfoReq.getProductId());
        //        logger.error(beError);
        //        throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        //    }
        //}
    }

    /**
     * 验证时间是否满足修改条件
     *
     * @param creditAdjustInfoReq
     * @param cmCustomerLimitInfo
     * @throws TxnException
     */
    private void checkDate(CreditAdjustInfoReq creditAdjustInfoReq, CmCustomerLimitInfo cmCustomerLimitInfo) throws TxnException {
        //当请求中失效日期和生效日期都存在
        if (null != creditAdjustInfoReq.getEffectiveDate() && null != creditAdjustInfoReq.getExpiryDate()) {
            //比较请求中失效日期和生效日期
            int compare = DateUtils.compare(creditAdjustInfoReq.getExpiryDate(), creditAdjustInfoReq.getEffectiveDate());
            if (compare == -1) {
                String dateError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_DATE, creditAdjustInfoReq.getProductId());
                //当请求中的失效日期小与生效日期抛出异常结束
                logger.error(dateError);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
        }
        //当请求中生效日期存在，失效日期不存在
        String dataError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_DATE_END, creditAdjustInfoReq.getProductId());
        if (null != creditAdjustInfoReq.getEffectiveDate() && null == creditAdjustInfoReq.getExpiryDate()) {
            //将报文中生效日期与数据中失效日期比较
            int compare = DateUtils.compare(cmCustomerLimitInfo.getLimitExpiryDate(), creditAdjustInfoReq.getEffectiveDate());
            if (compare == -1) {
                //将报文中生效日期大于数据中失效日期---抛出异常结束
                logger.error(dataError);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
        }

        //当请求中失效日期存在，生效日期不存在
        if (null != creditAdjustInfoReq.getExpiryDate() && null == creditAdjustInfoReq.getEffectiveDate()) {
            //将报文中失效日期与数据中生效日期比较
            int compare = DateUtils.compare(creditAdjustInfoReq.getExpiryDate(), cmCustomerLimitInfo.getLimitEffectiveDate());
            if (compare == -1) {
                //将报文中失效日期小于数据中生效日期---抛出异常结束
                logger.error(dataError);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
        }
    }

    /**
     * （额度调整,修改额度状态）额度节点属性为非循环时 验证合同号
     *
     * @param contractNumber
     * @param cmCustomerLimitInfos
     * @param productId
     * @throws TxnBizException
     */
    private CmCustomerLimitInfo checkContractNumber(String contractNumber, List<CmCustomerLimitInfo> cmCustomerLimitInfos, String productId, String type) throws TxnBizException {
        CmCustomerLimitInfo customerLimit = new CmCustomerLimitInfo();
        //当额度节点类型为非循环额度时 合同号不能为空
        if (StringUtils.isBlank(contractNumber)) {
            logger.error("额度节点属性为非循环时，接口传送的合同号为空");
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
        boolean existContractNumber = false;
        //额度中是否存在该合同号 对应的节点
        for (CmCustomerLimitInfo customerLimitInfo : cmCustomerLimitInfos) {
            if (customerLimitInfo.getContractNumber().equalsIgnoreCase(contractNumber)) {
                existContractNumber = true;
                customerLimit = customerLimitInfo;
                break;
            }
        }
        //非循环客户额度 合同号不匹配
        if (!existContractNumber && CustomerConstant.CUSTOMER_TYPE_CREDITADJUST.equalsIgnoreCase(type)) {
            //额度调整
            String limitError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_CONTNUMBER, productId);
            logger.error(limitError);
            throw new TxnBizException(TxnRespCode.BIZ_ERROR.getCode(), TxnRespCode.BIZ_ERROR.getUserTips());
        }
        if (!existContractNumber && CustomerConstant.CUSTOMER_TYPE_CREDITFREEZE.equalsIgnoreCase(type)) {
            //调整额度状态（冻结/解冻）
            String contractNumberError = String.format(ErrorMsgConstant.CREDIT_PARAMLIST_FREEZE_CONTROCTNUMBER, productId, contractNumber);
            logger.error(contractNumberError);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        return customerLimit;
    }

    /**
     * 额度(调整，冻结/解冻)时当额度类型为循环额度时验证产品是否重复
     *
     * @param productMap
     * @param productId
     * @return
     * @throws TxnException
     */
    private void checkProduct(HashMap<String, String> productMap, String productId, String type) throws TxnException {
        if (null != productMap.get(productId) && CustomerConstant.CUSTOMER_TYPE_CREDITFREEZE.equalsIgnoreCase(type)) {
            // 额度冻结/解冻时产品id重复
            String productError = String.format(ErrorMsgConstant.CREDIT_PARAMLIST_FREEZE_REPEAT, productId);
            logger.error(productError);
            throw new TxnException(TxnRespCode.DATA_EXISTS, TxnRespCode.DATA_EXISTS.getUserTips());
        }
        if (null != productMap.get(productId) && CustomerConstant.CUSTOMER_TYPE_CREDITADJUST.equalsIgnoreCase(type)) {
            //额度调整产品重复
            String productError = String.format(ErrorMsgConstant.CREDIT_PARAMLIST_ADJUST_REPEAT, productId);
            logger.error(productError);
            throw new TxnException(TxnRespCode.DATA_EXISTS, TxnRespCode.DATA_EXISTS.getUserTips());
        }
    }

    /**
     * 验证节点对应的客户额度是否存在
     *
     * @param cmCustomerLimitInfos
     * @param productId
     * @throws TxnException
     */
    private void checkCustomerLimitInfo(List<CmCustomerLimitInfo> cmCustomerLimitInfos, String productId) throws TxnException {
        //验证节点对应的客户额度是否存在
        if (cmCustomerLimitInfos == null || cmCustomerLimitInfos.isEmpty()) {
            String noError = String.format(ErrorMsgConstant.CREDIT_LIMIT_PRODUCT_ERROR, productId);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
    }

    /**
     * 验证 调整额度时 （授信额度，额度生效日期，额度失效日期）不能同时为空
     *
     * @param creditAdjustInfoReq
     * @throws TxnException
     */
    private void checkModifyValue(CreditAdjustInfoReq creditAdjustInfoReq) throws TxnException {
        String productId = creditAdjustInfoReq.getProductId();
        //验证 调整额度时 （授信额度，额度生效日期，额度失效日期）不能同时为空
        if (null == creditAdjustInfoReq.getCreditLimit() && null == creditAdjustInfoReq.getEffectiveDate() && null == creditAdjustInfoReq.getExpiryDate()) {
            //同时为空抛出异常结束
            String limitError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_NODATA, productId);
            logger.error(limitError);
            throw new TxnException(TxnRespCode.NOT_EMPTY, TxnRespCode.NOT_EMPTY.getUserTips());
        }
    }


    private CmCustomerLimitInfo buildModifyCustomerLimit(CmCustomerLimitInfo customerLimits, CreditFreeze creditFreeze, String userId) {
        //声明空的需要修改对象
        CmCustomerLimitInfo customerLimitInfo = new CmCustomerLimitInfo();
        //将修改状态的客户id放入修改对象
        customerLimitInfo.setId(customerLimits.getId());
        //将租户id放入修改条件
        customerLimitInfo.setTenantId(customerLimits.getTenantId());
        //当请求报文中为冻结 赋值给这状态为 2
        if (creditFreeze.getOperateType().equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_F)) {
            customerLimitInfo.setOcFlag(CustomerConstant.CREDIT_LIMIT_STATUS_2);
        }
        //当请求报文中为解冻 赋值给这状态为 1
        if (creditFreeze.getOperateType().equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_R)) {
            customerLimitInfo.setOcFlag(CustomerConstant.CREDIT_LIMIT_STATUS_1);
        }
        //将原有状态放在此字段
        customerLimitInfo.setLimitStatus(customerLimits.getLimitStatus());
        //将请求报文中userId放入修改人中
        customerLimitInfo.setUpdateBy(userId);
        //将历史记录所需属性放入对象中
        customerLimitInfo.setChannel(customerLimits.getChannel());
        //将机构号放入
        customerLimitInfo.setOrganizationId(customerLimits.getOrganizationId());
        //将客户id放入
        customerLimitInfo.setCustomerId(customerLimits.getCustomerId());
        //冻结解冻添加到调额日子表添加字段
        customerLimitInfo.setSeqNo(customerLimits.getSeqNo());
        customerLimitInfo.setLimitId(customerLimits.getLimitId());
        customerLimitInfo.setTemplateId(customerLimits.getTemplateId());
        customerLimitInfo.setLimitPerm(customerLimits.getLimitPerm());

        return customerLimitInfo;
    }

    private CmCustomerLimitInfo buildModifyCustomerLimitOfCmsClient(CmCustomerLimitInfo customerLimits, CreditFreezeOfCmsReq creditFreeze, String userId) {
        //声明空的需要修改对象
        CmCustomerLimitInfo customerLimitInfo = new CmCustomerLimitInfo();
        //将修改状态的客户id放入修改对象
        customerLimitInfo.setId(customerLimits.getId());
        //将租户id放入修改条件
        customerLimitInfo.setTenantId(customerLimits.getTenantId());
        //当请求报文中为冻结 赋值给这状态为 2
        if (creditFreeze.getFlag().equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_F)) {
            customerLimitInfo.setOcFlag(CustomerConstant.CREDIT_LIMIT_STATUS_2);
        }
        //当请求报文中为解冻 赋值给这状态为 1
        if (creditFreeze.getFlag().equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_R)) {
            customerLimitInfo.setOcFlag(CustomerConstant.CREDIT_LIMIT_STATUS_1);
        }
        //当请求报文中为注销 赋值给这状态为 0
        if (creditFreeze.getFlag().equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_C)) {
            customerLimitInfo.setOcFlag(CustomerConstant.CREDIT_LIMIT_STATUS_4);
        }
        //将原有状态放在此字段
        customerLimitInfo.setLimitStatus(customerLimits.getLimitStatus());
        //将请求报文中userId放入修改人中
        customerLimitInfo.setUpdateBy(userId);
        //将历史记录所需属性放入对象中
        customerLimitInfo.setChannel(customerLimits.getChannel());
        //将机构号放入
        customerLimitInfo.setOrganizationId(customerLimits.getOrganizationId());
        //将客户id放入
        customerLimitInfo.setCustomerId(customerLimits.getCustomerId());
        // 客户额度有效期
        customerLimitInfo.setLimitExpiryDate(customerLimits.getLimitExpiryDate());
        return customerLimitInfo;
    }


    private CreditInfoRes buildCreditInfo(CmCustomerLimitInfo customerLimitInfo,Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap,String productId) {
        CreditInfoRes creditInfoRes = new CreditInfoRes();

        BigDecimal limitAvailable = customerLimitInfo.getLimitAvailable();
        BigDecimal limitUsed = customerLimitInfo.getLimitUsed();
        //可用额度需要按照父节点获取最小可用额度
        String fatherLimitId = customerLimitInfo.getFatherId();
        //父节点处理
        while (StringUtils.isNotBlank(fatherLimitId)) {
            List<CmCustomerLimitInfo> fatherInfos = customerLimitInfoMap.get(fatherLimitId);
            if (CollectionUtils.isNotEmpty(fatherInfos)) {
                // 同一额度id的叶子节点，父节点只会有一个
                CmCustomerLimitInfo fatherInfo = fatherInfos.get(0);
                if(fatherInfo.getAuthCheck().equals(CustomerConstant.AUTH_CHECK_1) &&
                        limitAvailable.compareTo(fatherInfo.getLimitAvailable()) > 0 ){
                    limitAvailable = fatherInfo.getLimitAvailable();
                }
                fatherLimitId = fatherInfo.getFatherId();
            } else {
                break;
            }
        }
        if (BizTypeProducts.PRODUCT_XYJY.equals(productId)) {
            CmCustomerLimitFroze record = new CmCustomerLimitFroze();
            record.setTenantId(customerLimitInfo.getTenantId());
            record.setCustomerId(customerLimitInfo.getCustomerId());
            record.setProductId(productId);
            record.setStatus(CustomerConstant.OPERATE_TYPE_F);
            List<CmCustomerLimitFroze> limitFrozeList = iCmCustomerLimitFrozeService.limitFrozeQuery(record);
            BigDecimal totalFrozeAmt = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(limitFrozeList)) {
                totalFrozeAmt = limitFrozeList.stream().map(cmCustomerLimitFroze -> {
                    return cmCustomerLimitFroze.getFrozeAmount().subtract(cmCustomerLimitFroze.getUsedAmount());
                }).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
            //可用额度=可用额度-总计冻结+总计冻结已用
            //已用额度=已用额度+总计冻结-总计冻结已用
            //可用额度放入返回对象中
            BigDecimal availAmt = limitAvailable.subtract(totalFrozeAmt);
            availAmt = availAmt.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : availAmt;
            creditInfoRes.setAvailCreditLimit(availAmt);
            //已使用额度放入返回对象中
            creditInfoRes.setLimitUsed(limitUsed.add(totalFrozeAmt));
        } else {
            //可用额度放入返回对象中
            creditInfoRes.setAvailCreditLimit(limitAvailable);
            //已使用额度放入返回对象中
            creditInfoRes.setLimitUsed(limitUsed);
        }
        //授信额度放入返回对象中
        creditInfoRes.setCreditLimit(customerLimitInfo.getLimitAmount());
        //额度生效日期放入返回对象中
        creditInfoRes.setEffectiveDate(customerLimitInfo.getLimitEffectiveDate());
        //额度失效日期放入返回对象中
        creditInfoRes.setExpiryDate(customerLimitInfo.getLimitExpiryDate());
        creditInfoRes.setContractNumber(customerLimitInfo.getContractNumber());
        creditInfoRes.setSeqNo(customerLimitInfo.getSeqNo());
        return creditInfoRes;
    }

    private CreditInfoRes buildCreditInfo(CmCustomerLimitInfo customerLimitInfo,Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap,PrProductTable product) {
        CreditInfoRes creditInfoRes = buildCreditInfo(customerLimitInfo, customerLimitInfoMap,product.getProductId());
        creditInfoRes.setProductType(product.getTableType());
        creditInfoRes.setProductId(product.getProductId());
        creditInfoRes.setLimitId(customerLimitInfo.getLimitId());
        creditInfoRes.setLimitStatus(customerLimitInfo.getLimitStatus());
        creditInfoRes.setCmCustomerLimitInfo(customerLimitInfo);
        return creditInfoRes;
    }

    /**
     * 额度树查询验证证件类型和证件号手否同时存在或不存在,验证客户id与证件是否存在一个
     *
     * @author Qingyao Xing
     * @date 2019/8/21 9:36
     */
    private void verifyIdNumberAndType(String idNumber, String idType, String customerId) throws TxnException {
        //验证证件号和证件类型是否同时（存在/不存在）
        if (StringUtils.isBlank(idNumber) && StringUtils.isNotBlank(idType)) {
            logger.error(ErrorMsgConstant.CREDIT_GETTREE_PARAM_ID);
            throw new TxnException(TxnRespCode.NOT_EMPTY, TxnRespCode.NOT_EMPTY.getUserTips());
        }
        if (StringUtils.isNotBlank(idNumber) && StringUtils.isBlank(idType)) {
            logger.error(ErrorMsgConstant.CREDIT_GETTREE_PARAM_ID);
            throw new TxnException(TxnRespCode.NOT_EMPTY, TxnRespCode.NOT_EMPTY.getUserTips());
        }
        //（客户id）/(证件类型+证件号码)必须存在一个条件
        if (StringUtils.isBlank(customerId) && StringUtils.isBlank(idNumber)) {
            logger.error(ErrorMsgConstant.CREDIT_GETTREE_PARAM_NO);
            throw new TxnException(TxnRespCode.NOT_EMPTY, TxnRespCode.NOT_EMPTY.getUserTips());
        }
    }

    /**
     * 验证客户额度冻结标志是否正确，及与数据库当前状态是否冲突
     *
     * @author Qingyao Xing
     * @date 2019/8/21 14:41
     */
    private void verifyOperateType(String operateType, String status, String productId) throws TxnException {
        //若api入参-冻结/解冻标志既不为冻结，也不为解冻时
        if (!operateType.equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_F) && !operateType.equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_R)) {
            String paramError = String.format(ErrorMsgConstant.CUSTOMER_OPERATETYPE_PARAM_ERROR, productId);
            logger.error(paramError);
            throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        }
        if (status.equalsIgnoreCase(CustomerConstant.CREDIT_LIMIT_STATUS_0)) {
            logger.error(ErrorMsgConstant.CUSTOMER_STATUS_ERROR);
            throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        }
        //若api入参-冻结/解冻标志为冻结，则客户额度记录的状态必须为正常；
        String statusError = String.format(ErrorMsgConstant.CUSTOMER_OPERATETYPE_STATUS_ERROR, productId);
        if (operateType.equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_F) && !status.equalsIgnoreCase(CustomerConstant.CREDIT_LIMIT_STATUS_1)) {
            logger.error(statusError);
            throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        }
        //若api入参-冻结/解冻标志为解冻，则客户额度记录的状态必须为冻结
        if (operateType.equalsIgnoreCase(CustomerConstant.CREDIT_OPERATETYPE_R) && !status.equalsIgnoreCase(CustomerConstant.CREDIT_LIMIT_STATUS_2)) {
            logger.error(statusError);
            throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        }
    }

    /**
     * 验证客户额度是否存在和状态是否可用
     *
     * @author Qingyao Xing
     * @date 2019/8/7 10:21
     */
    private void checkCmCustomerLimitStatus(CmCustomerLimitInfo cmCustomerLimitInfo, String productId) throws TxnException {
        //判断客户额度对象是否存在
        if (null == cmCustomerLimitInfo) {
            //对象不存在 抛出异常结束
            String customerError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_LIMIT_NODATA, productId);
            logger.error(customerError);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());

        }
        //判断客户额度信息状态是否正常和以及是注销状态
        if (!Arrays.asList(CustomerConstant.STATUS_NORMAL,CustomerConstant.STATUS_CANCEL).contains(cmCustomerLimitInfo.getLimitStatus())) {
            //客户额度信息非有效 抛出异常结束
            String statusError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_LIMIT_STATUS, productId);
            logger.error(statusError);
            throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
        }
    }

    /**
     * 客户额度记录中的已使用额度=0 and 实时还款未入账金额=0 and 可用额度=当前生效额度；
     *
     * @author Qingyao Xing
     * @date 2019/8/16 13:53
     */
    private boolean compareLimit(CmCustomerLimitInfo cmCustomerLimitInfo) {
        return cmCustomerLimitInfo.getLimitUsed().compareTo(BigDecimal.valueOf(0)) != 0 || cmCustomerLimitInfo.getOnlinePaymentAmount().compareTo(BigDecimal.valueOf(0)) != 0 || cmCustomerLimitInfo.getLimitAvailable().compareTo(cmCustomerLimitInfo.getLimitAmount()) != 0;
    }

    /**
     * 获取需要修改的枝节点的数据集合
     *
     * @author Qingyao Xing
     * @date 2019/8/7 13:56
     */
    private List<CmCustomerLimitInfo> getLimitModifyList(Map<String, BigDecimal> fatherLimitModifyList, Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap, String organizationId, String customerId, String userId, String channel) {
        //声明空的修改额度的集合
        List<CmCustomerLimitInfo> list = new ArrayList<>();
        //遍历需要更改额度的枝节点集合
        for (Map.Entry<String, BigDecimal> limit : fatherLimitModifyList.entrySet()) {
            //声明一个空的对象
            CmCustomerLimitInfo customerLimitInfo = new CmCustomerLimitInfo();
            //获取节点id
            String key = limit.getKey();
            //根据节点id获取额度数据
            List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(key);
            CmCustomerLimitInfo cmCustomerLimitInfo = cmCustomerLimitInfos.get(0);
            //将请求报文中userId放入修改人中
            customerLimitInfo.setUpdateBy(userId);
            //插入主键，客户id，渠道号，节点id，机构号
            customerLimitInfo.setId(cmCustomerLimitInfo.getId());
            customerLimitInfo.setTenantId(cmCustomerLimitInfo.getTenantId());
            customerLimitInfo.setCustomerId(customerId);
            customerLimitInfo.setChannel(channel);
            customerLimitInfo.setLimitId(key);
            customerLimitInfo.setOrganizationId(organizationId);
            //获得枝节点调整后的额度
            BigDecimal limitPerm = cmCustomerLimitInfo.getLimitAmount().add(limit.getValue());
            //将调整后的额度放入当前生效额度内
            customerLimitInfo.setLimitAmount(limitPerm);
            //将原有额度放在永久额度字段中
            customerLimitInfo.setLimitPerm(limitPerm);
            //获取调整后的可用额度
            BigDecimal limitAvailable = limitPerm.subtract(cmCustomerLimitInfo.getLimitUsed()).subtract(cmCustomerLimitInfo.getOnlinePaymentAmount());
            BigDecimal available = limitAvailable.compareTo(BigDecimal.ZERO) > 0 ? limitAvailable : BigDecimal.ZERO;
            //将新的可用额度放在可用额度中
            customerLimitInfo.setLimitAvailable(available);
            customerLimitInfo.setRecordVersionNumber(cmCustomerLimitInfo.getRecordVersionNumber());
            customerLimitInfo.setLimitStatus(CustomerConstant.CREDIT_LIMIT_STATUS_1);
            //将实体放入集合中
            list.add(customerLimitInfo);
        }
        //返回集合
        return list;
    }




    /**
     * 授信功能验证集合中productId和合同号不能重复  ,验证时间
     *
     * @param lists 授信信息列表creditInfoList
     * @return List<String>  授信信息列表的产品集合
     * @author Qingyao Xing
     * @date 2019/7/23 14:35
     */
    private void provingProduct(List<CreditInfoReq> lists) throws TxnException {
        //请求报文中的产品map
        Map<String, String> productMap = Maps.newHashMap();
        //请求报文中的合同号map
        Map<String, String> numberMap = Maps.newHashMap();
        for (CreditInfoReq creditInfo : lists) {
            String productId = creditInfo.getProductId();
            if (null != productMap.get(productId)) {
                logger.error(ErrorMsgConstant.CREDIT_PARAMLIST_REPEAT);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            productMap.put(productId, productId);
            //验证失效时间应大于生效时间
            int compare = DateUtils.compare(creditInfo.getExpiryDate(), creditInfo.getEffectiveDate());
            if (compare == -1) {
                //当生效时间大于失效时间时抛出异常
                String dateError = String.format(ErrorMsgConstant.CREDIT_CUSTOMER_PARAM_DATE, productId);
                logger.error(dateError);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            //请求报文中合同号是否已存在
            String contractNumber = creditInfo.getContractNumber();
            if (StringUtils.isNotBlank(contractNumber)) {
                if (null != numberMap.get(contractNumber)) {
                    logger.error(ErrorMsgConstant.CREDIT_PARAMLIST_REPEAT);
                    throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
                }
                numberMap.put(contractNumber, contractNumber);
            }
        }
    }

    /**
     * 父节点赋值生成CustomerLimitInfo
     * @param info
     * @param prCreditLimit
     * @param allCustomerLimitInfoList 客户已有额度节点
     * @return
     * @throws TxnException
     */
    private CmCustomerLimitInfo addFatherLimitInfo(CmCustomerLimitInfo info,
                                                   PrCreditLimit prCreditLimit,
                                                   List<CmCustomerLimitInfo> allCustomerLimitInfoList,
                                                   Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap) throws TxnException {
        //基本属性赋值
        CmCustomerLimitInfo limitInfo = BeanMapping.copy(info, CmCustomerLimitInfo.class);

        String ocFlag = prCreditLimit.getOcFlag();

        //如果父节点额度存在，存在使用原属性，不存在使用默认值
        List<CmCustomerLimitInfo> list = customerLimitInfoMap.get(prCreditLimit.getLimitId());
        CmCustomerLimitInfo father = null;
        if(list!=null) {
            father = list.get(0);
        }
        if(father!=null){
            limitInfo = father;
        }else{
            //添加默认值
            //授信额度--请求报文（授信金额）
            limitInfo.setLimitAmount(BigDecimal.ZERO);
            //可用额度--请求报文（授信金额）
            limitInfo.setLimitAvailable(BigDecimal.ZERO);
            //永久额度--请求报文（授信金额）
            limitInfo.setLimitPerm(BigDecimal.ZERO);
            //已用额度--请求报文（授信金额）
            limitInfo.setLimitUsed(BigDecimal.ZERO);
            //流水号（默认为空）
            limitInfo.setSeqNo("");
            //临时额度 (授信是为0)
            limitInfo.setLimitTemp(BigDecimal.ZERO);
            //实时还款未入账金额 (授信是为0)
            limitInfo.setOnlinePaymentAmount(BigDecimal.ZERO);
            //版本号 (默认为1)
            limitInfo.setRecordVersionNumber(Constant.VERSION_NUMBER);
            if(Constant.NO_FLAG.equals(prCreditLimit.getNeedActive())){
                //需激活为0 授信即生效
                limitInfo.setLimitStatus(CustomerConstant.STATUS_NORMAL);
            }else{
                //需激活为1 授信为未激活状态
                limitInfo.setLimitStatus(CustomerConstant.STATUS_NOT_ACTIVE);
            }

            //添加来自节点模版的数据

            //检核标识 --(额度模板表:检核标识)
            limitInfo.setAuthCheck(prCreditLimit.getAuthCheck());
            //继承比例--(额度模板表:占比)
            limitInfo.setObtainPercentage(prCreditLimit.getPercentage());
            //额度计算方式 0 输入 1 求和 2 最大值 3 最小值
            limitInfo.setOcFlag(ocFlag);
            //父节点额度编号--(额度模板表:上层额度节点id)
            limitInfo.setFatherId(prCreditLimit.getFatherLimitId());

            //添加来自节点的数据
            //中文名称--额度节点表(描述)
            limitInfo.setChineseName(prCreditLimit.getLimitDesc());
            //额度属性--额度节点表(使用方式)
            limitInfo.setUseMode(prCreditLimit.getUseMode());
            //额度类型--额度节点表(额度种类)
            limitInfo.setLimitClass(prCreditLimit.getLimitClass());
            //额度编号--额度节点表(额度节点id)
            limitInfo.setLimitId(prCreditLimit.getLimitId());
        }

        //根据额度计算方式计算
        if(CustomerConstant.OC_FLAG_0.equals(ocFlag)){//按照输入授信
            //如果父节点是按照输入，保留原来的值
            if(null != father){
                //授信额度
                limitInfo.setLimitAmount(father.getLimitAmount());
                //可用额度
                limitInfo.setLimitAvailable(father.getLimitAvailable());
                //永久额度
                limitInfo.setLimitPerm(father.getLimitPerm());
                //已用额度
                limitInfo.setLimitUsed(father.getLimitUsed());
            }
        }else if(CustomerConstant.OC_FLAG_1.equals(ocFlag)){//求和 获取所有字节点，计算总额度 + 本次授信额度
            BigDecimal totalLimitAmount = BigDecimal.ZERO;//授信额度
            BigDecimal totalAvaAmount = BigDecimal.ZERO;//可用额度
            if(CollectionUtils.isNotEmpty(allCustomerLimitInfoList)){
                //遍历父节点下所有子节点
                for(CmCustomerLimitInfo allLimitInfo : allCustomerLimitInfoList){
                    //判断是否是这个父节点下子节点额度，根据子节点额度进行额度计算
                    if(prCreditLimit.getLimitId().equals(allLimitInfo.getFatherId())){
                        totalLimitAmount = totalLimitAmount.add(allLimitInfo.getLimitAmount());
                        totalAvaAmount = totalAvaAmount.add(allLimitInfo.getLimitAvailable());
                    }
                }
            }
            //授信额度--各个子节点授信之和
            limitInfo.setLimitAmount(totalLimitAmount);
            //可用额度--各个子节点可用额度之和
            limitInfo.setLimitAvailable(totalAvaAmount);
            //永久额度--各个子节点授信之和
            limitInfo.setLimitPerm(totalLimitAmount);
        }else if(CustomerConstant.OC_FLAG_2.equals(ocFlag)){
            BigDecimal totalLimitAmount = BigDecimal.ZERO;//授信额度
            BigDecimal totalAvaAmount = BigDecimal.ZERO;//可用额度
            if(CollectionUtils.isNotEmpty(allCustomerLimitInfoList)){
                for(CmCustomerLimitInfo allLimitInfo : allCustomerLimitInfoList){
                    if(prCreditLimit.getLimitId().equals(allLimitInfo.getFatherId())){
                        if(totalLimitAmount.compareTo(allLimitInfo.getLimitAmount()) < 0){
                            totalLimitAmount = allLimitInfo.getLimitAmount();
                        }
                    }
                }
                //可用额度
                BigDecimal avaAmount = totalLimitAmount;
                for(CmCustomerLimitInfo oldInfo : allCustomerLimitInfoList){
                    if(prCreditLimit.getLimitId().equals(oldInfo.getFatherId())){
                        //总可用额度 = 总额度 - 所有子节点已用额度
                        avaAmount = avaAmount.subtract(oldInfo.getLimitUsed());
                    }
                }
                //可用额度
                totalAvaAmount = avaAmount;

            }
            //授信额度--各个子节点授信最大值
            limitInfo.setLimitAmount(totalLimitAmount);
            //可用额度--各个子节点可用额度之和
            limitInfo.setLimitAvailable(totalAvaAmount);
            //永久额度--各个子节点授信最大值
            limitInfo.setLimitPerm(totalLimitAmount);
        }else if(CustomerConstant.OC_FLAG_3.equals(ocFlag)){
            BigDecimal totalLimitAmount = BigDecimal.ZERO;//授信额度
            BigDecimal totalAvaAmount = BigDecimal.ZERO;//可用额度
            if(CollectionUtils.isNotEmpty(allCustomerLimitInfoList)){
                for(CmCustomerLimitInfo oldInfo : allCustomerLimitInfoList){
                    if(prCreditLimit.getLimitId().equals(oldInfo.getFatherId())){
                        if(totalLimitAmount.compareTo(oldInfo.getLimitAmount()) > 0){
                            totalLimitAmount = oldInfo.getLimitAmount();
                        }
                    }
                }
                //可用额度
                BigDecimal avaAmount = totalLimitAmount;
                for(CmCustomerLimitInfo oldInfo : allCustomerLimitInfoList){
                    //总可用额度 = 总额度 - 所有子节点已用额度
                    avaAmount = avaAmount.subtract(oldInfo.getLimitUsed());
                }

                if(avaAmount.compareTo(BigDecimal.ZERO) < 0){
                    avaAmount = BigDecimal.ZERO;
                }
                totalAvaAmount = avaAmount;

            }
            //授信额度--各个子节点授信最小值
            limitInfo.setLimitAmount(totalLimitAmount);
            //可用额度
            limitInfo.setLimitAvailable(totalAvaAmount);
            //永久额度--各个子节点授信最大值
            limitInfo.setLimitPerm(totalLimitAmount);
        }
        return limitInfo;
    }

    /**
     * 合并添加客户额度集合
     *
     * @param operationCustomerLimitList
     * @return
     */
    private List<CmCustomerLimitInfo> mergeCmCustomerLimitInfos(List<CmCustomerLimitInfo> operationCustomerLimitList) {
        //根据额度节点id 分组客户额度添加集合
        Map<String, List<CmCustomerLimitInfo>> customerLimitGroup = operationCustomerLimitList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));
        //根据额度节点id 合并客户额度获取该节点额度变化值总和
        Map<String, BigDecimal> limitMap = operationCustomerLimitList.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId, CollectorsUtils.summingBigDecimal(CmCustomerLimitInfo::getLimitAmount)));
        List<CmCustomerLimitInfo> customerLimitList = new ArrayList<>();
        customerLimitGroup.forEach((String limitId, List<CmCustomerLimitInfo> limitInfos) -> {
            CmCustomerLimitInfo cmCustomerLimitInfo = limitInfos.get(0);
            BigDecimal limit = limitMap.get(limitId);
            cmCustomerLimitInfo.setLimitAmount(limit);
            cmCustomerLimitInfo.setLimitPerm(limit);
            cmCustomerLimitInfo.setLimitAvailable(limit);
            customerLimitList.add(cmCustomerLimitInfo);
        });
        return customerLimitList;
    }


    private CmCustomerLimitInfo getCreditAdjustInfoModalOfCms(CreditFreezeOfCmsReq creditFreezeOfCmsReq, String organizationId, CmCustomerLimitInfo cmCustomerLimitInfo, String customerId, String limitId, String channel, String userId) {

        //声明空的客户额度
        CmCustomerLimitInfo customerLimitInfo = new CmCustomerLimitInfo();
        //将主键，渠道号，节点id，客户id，机构号赋值给新的客户额度req实体
        customerLimitInfo.setId(cmCustomerLimitInfo.getId());
        customerLimitInfo.setTenantId(cmCustomerLimitInfo.getTenantId());
        customerLimitInfo.setOrganizationId(organizationId);
        customerLimitInfo.setLimitId(limitId);
        customerLimitInfo.setCustomerId(customerId);
        customerLimitInfo.setChannel(channel);
        customerLimitInfo.setTemplateId(cmCustomerLimitInfo.getTemplateId());
        customerLimitInfo.setFatherId(cmCustomerLimitInfo.getFatherId());
        customerLimitInfo.setLimitUsed(cmCustomerLimitInfo.getLimitUsed());
        //判断请求报文中的调整后额度是否存在
        if (null != creditFreezeOfCmsReq.getLimitAmount()) {
            //调整后额度存在
            //将调整后额度放在当前生效额度字段中
            customerLimitInfo.setLimitAmount(new BigDecimal(creditFreezeOfCmsReq.getLimitAmount()));
            //将调整后额度放在永久额度字段中
            customerLimitInfo.setLimitPerm(new BigDecimal(creditFreezeOfCmsReq.getLimitAmount()));
            //获取额度调整后叶节点的可用额度
            BigDecimal limitAvailable = new BigDecimal(creditFreezeOfCmsReq.getLimitAmount()).subtract(cmCustomerLimitInfo.getLimitUsed()).subtract(cmCustomerLimitInfo.getOnlinePaymentAmount());
            //将新的可用额度放在临时额度中
            customerLimitInfo.setLimitAvailable(limitAvailable);
        }

        //将报文中userId放入修改人中
        customerLimitInfo.setUpdateBy(userId);
        customerLimitInfo.setRecordVersionNumber(cmCustomerLimitInfo.getRecordVersionNumber());
        return customerLimitInfo;
    }

    /**
     * cms页面客户端的客户额度冻结/解冻/调额
     * xuhui.gao
     *
     * @param creditFreezeReq 额度冻结，解冻,调额请求报文
     * @throws TxnException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<CustomerLimitInfoRes> modifyCreditFreezeByCmsClient(CreditFreezeOfCmsReq creditFreezeReq) throws TxnException {
        logger.debug("cms页面相关客户额度冻结/解冻/调额操作开始");
        List<CmCustomerLimitInfo> list = new ArrayList<>();//待处理额度信息
        List<CustomerLimitInfoRes> returnList = new ArrayList<>();//返回的额度信息
        //声明空的调额历史表的对象集合
        List<CmLimitAdjustLog> limitAdjustLogList = new ArrayList<>();
        //根据organizationId,channel,limitId,customerId,tenantId查询客户额度记录
        CmCustomerLimitInfo cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(creditFreezeReq.getCustomerId(), creditFreezeReq.getOrganizationId(), creditFreezeReq.getLimitId(),
                creditFreezeReq.getContractNumber(), creditFreezeReq.getTenantId());
        if (cmCustomerLimitInfo == null) {
            logger.error(ErrorMsgConstant.CUSTOMER_QUERY_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        String organizationId = cmCustomerLimitInfo.getOrganizationId();
        String tenantId = cmCustomerLimitInfo.getTenantId();
        //获取业务日期
        PrOrganizationTable org = organizationTableService.findByOrId(organizationId, tenantId);
        Date businessDate = org.getNextProcessingDate();

        //冻结请求
        if ("F".equals(creditFreezeReq.getFlag())) {
            //检查额度节点是否为正常状态，非正常状态不允许冻结
            if (!"1".equals(cmCustomerLimitInfo.getLimitStatus())) {
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            limitAdjustLogList.add(this.buildCmLimitAdjustLogOfCms(creditFreezeReq, cmCustomerLimitInfo, creditFreezeReq.getUserId(), businessDate));
            //创建修改的对象
            CmCustomerLimitInfo customerLimitInfo = this.buildModifyCustomerLimitOfCmsClient(cmCustomerLimitInfo, creditFreezeReq, creditFreezeReq.getUserId());
            //将对象放入集合中
            list.add(customerLimitInfo);
            //修改客户额度标志
            iCmCustomerLimitInfoService.modifyCustomerLimitStatus(list);
            if (limitAdjustLogList.size() > 0) {
                for (CmLimitAdjustLog cmLimitAdjustLog : limitAdjustLogList) {
                    cmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
                }
            }
            CmCustomerLimitInfo returnCmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(creditFreezeReq.getCustomerId(), organizationId, creditFreezeReq.getLimitId(),
                    creditFreezeReq.getContractNumber(),
                    tenantId);
            CustomerLimitInfoRes customerLimitInfoRes = BeanMapping.copy(returnCmCustomerLimitInfo, CustomerLimitInfoRes.class);
            returnList.add(customerLimitInfoRes);
            return returnList;
        } else if ("R".equals(creditFreezeReq.getFlag())) {
            //解冻请求
            //检查额度节点是否为冻结状态，非冻结状态不允许解冻
            if (!"2".equals(cmCustomerLimitInfo.getLimitStatus())) {
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            limitAdjustLogList.add(this.buildCmLimitAdjustLogOfCms(creditFreezeReq, cmCustomerLimitInfo, creditFreezeReq.getUserId(),businessDate));
            //创建修改的对象
            CmCustomerLimitInfo customerLimitInfo = this.buildModifyCustomerLimitOfCmsClient(cmCustomerLimitInfo, creditFreezeReq, creditFreezeReq.getUserId());
            //将对象放入集合中
            list.add(customerLimitInfo);
            //修改客户额度标志
            iCmCustomerLimitInfoService.modifyCustomerLimitStatus(list);
            if (limitAdjustLogList != null && limitAdjustLogList.size() > 0) {
                for (CmLimitAdjustLog cmLimitAdjustLog : limitAdjustLogList) {
                    cmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
                }
            }
            CmCustomerLimitInfo returnCmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(creditFreezeReq.getCustomerId(),creditFreezeReq.getOrganizationId(),creditFreezeReq.getLimitId(),creditFreezeReq.getContractNumber(),creditFreezeReq.getTenantId());
            CustomerLimitInfoRes customerLimitInfoRes = BeanMapping.copy(returnCmCustomerLimitInfo,CustomerLimitInfoRes.class);
            returnList.add(customerLimitInfoRes);
            return returnList;
        } else if ("C".equals(creditFreezeReq.getFlag())) {
            //检查额度节点非注销状态不允许再次注销
            if (CustomerConstant.CREDIT_LIMIT_STATUS_4.equals(cmCustomerLimitInfo.getLimitStatus())) {
                throw new TxnException(TxnRespCode.BIZ_ERROR, "已注销，不能再次注销");
            }
            // 授信合同号
            String contractNumber = cmCustomerLimitInfo.getContractNumber();
            String customerId = creditFreezeReq.getCustomerId();
            CmLimitChangeLog queryCon = new CmLimitChangeLog();
            queryCon.setTenantId(org.getTenantId());
            queryCon.setCustomerId(customerId);
            List<CmLimitChangeLog> queryLogs = extCmLimitChangeLogMapper.queryByCon(queryCon);
            Optional<CmLimitChangeLog> optional = queryLogs.stream()
                    //.filter(log -> Objects.isNull(log.getEtlSettleDate()))
                    .filter(log -> contractNumber.equalsIgnoreCase(log.getContractNumber()))
                    .findAny();
            // 是否发送api-server修改
            AtomicBoolean sendFlag = new AtomicBoolean(false);
            // 修改的类型
            AtomicReference<String> bizType = new AtomicReference<>("1");
            AtomicReference<String> userId = new AtomicReference<>("");
            if (optional.isPresent()) {
                String etlContractNumber = optional.get().getEtlContractNumber();
                List<CmLimitChangeLog> logs = queryLogs.stream().filter(log -> etlContractNumber.equalsIgnoreCase(log.getEtlContractNumber())).collect(Collectors.toList());
                List<CmCustomerLimitInfo> cmCustomerLimitInfos = extCmCustomerLimitInfoMapper.selectCusLimitByCusIdAndOrgIdAndStatus(organizationId, customerId, CustomerConstant.STATUS_CANCEL, tenantId);
                for (CmLimitChangeLog log : logs) {
                    Optional<CmCustomerLimitInfo> limitInfoOptional = cmCustomerLimitInfos.stream().filter(limit -> log.getContractNumber().equalsIgnoreCase(limit.getContractNumber())).findAny();
                    if (limitInfoOptional.isPresent()) {
                        CmCustomerLimitInfo limitInfo = limitInfoOptional.get();
                        // 查询客户的激活信息
                        CmCustomerExtInfo info = new CmCustomerExtInfo();
                        String productId = log.getProductId();
                        info.setChannel(log.getChannel());
                        info.setTenantId(log.getTenantId());
                        info.setCustomerId(log.getCustomerId());
                        info.setProductId(productId);
                        CmCustomerExtInfo extInfo = extCmCustomerExtInfoMapper.selectByConditions(info);
                        if (Objects.nonNull(extInfo) && Constant.YES_FLAG.equalsIgnoreCase(extInfo.getProductActiveFlag())) {
                            CmCustomerLimitInfo cancelCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(customerId, creditFreezeReq.getOrganizationId(), limitInfo.getLimitId(),
                                    log.getContractNumber(), creditFreezeReq.getTenantId());
                            limitAdjustLogList.add(this.buildCmLimitAdjustLogOfCms(creditFreezeReq, cancelCustomerLimitInfo, creditFreezeReq.getUserId(), businessDate));
                            //创建修改的对象
                            CmCustomerLimitInfo customerLimitInfo = this.buildModifyCustomerLimitOfCmsClient(cancelCustomerLimitInfo, creditFreezeReq, creditFreezeReq.getUserId());
                            // 还在有效期内需往回缩
                            if (log.getExpiryDate().compareTo(businessDate) > 0) {
                                customerLimitInfo.setLimitExpiryDate(businessDate);
                            }
                            //将对象放入集合中
                            list.add(customerLimitInfo);
                            // 修改产品激活标志
                            CmCustomerExtInfo res = new CmCustomerExtInfo();
                            res.setId(extInfo.getId());
                            res.setTenantId(log.getTenantId());
                            res.setProductActiveFlag("2");
                            res.setUpdateBy(res.getUserId());
                            res.setRecordVersionNumber(extInfo.getRecordVersionNumber() + 1);
                            extCmCustomerExtInfoMapper.updateProductActiveFlag(res);
                            Date limitExpiryDate = customerLimitInfo.getLimitExpiryDate();
                            // 更新综合额度表信息
                            CmLimitChangeLog updateLog = new CmLimitChangeLog();
                            updateLog.setId(log.getId());
                            updateLog.setCustomerId(customerId);
                            updateLog.setTenantId(log.getTenantId());
                            updateLog.setExpiryDate(limitExpiryDate);
                            Date etlExpiryDate = log.getEtlExpiryDate();
                            // 取小值
                            updateLog.setEtlExpiryDate(etlExpiryDate.after(businessDate) ? businessDate : etlExpiryDate);
                            extCmLimitChangeLogMapper.updateByPrimaryKeySelective(updateLog);
                            userId.set(extInfo.getUserId());
                            sendFlag.set(true);
                            bizType.set(BizTypeProducts.getBizType(productId).getCode());
                        }
                    }
                }

            }
            // 修改客户额度标志
            iCmCustomerLimitInfoService.modifyCustomerLimitStatus(list);
            if (limitAdjustLogList.size() > 0) {
                for (CmLimitAdjustLog cmLimitAdjustLog : limitAdjustLogList) {
                    cmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
                }
            }
            // 是否通知api-server修改额度状态
            if (sendFlag.get()) {
                LimitLogoutRequest logoutRequest = new LimitLogoutRequest();
                logoutRequest.setReqSeq(SeqUtils.getSN());
                logoutRequest.setChannelNo(creditFreezeReq.getChannel());
                logoutRequest.setUserId(userId.get());
                logoutRequest.setTenantId(creditFreezeReq.getTenantId());
                logoutRequest.setBizType(bizType.get());
                ApiResponse logoutResponse;
                try {
                    logoutResponse = iUserApiService.logout(JsonUtils.toJSon(logoutRequest));
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new TxnBizException(TxnRespCode.LOGOUT_CODE_ERROR);
                }
                if (Objects.isNull(logoutResponse)) {
                    throw new TxnBizException(TxnRespCode.LOGOUT_CODE_ERROR);
                }
                if (TxnRespCode.SUCCESS.getCode() != logoutResponse.getCode()) {
                    String errorMsg = null == logoutResponse.getMessage() ? TxnRespCode.LOGOUT_CODE_ERROR.getMsg() : logoutResponse.getMessage();
                    throw new TxnBizException(errorMsg);
                }
            }

            CmCustomerLimitInfo returnCmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(customerId, creditFreezeReq.getOrganizationId(), creditFreezeReq.getLimitId(),
                    creditFreezeReq.getContractNumber(), creditFreezeReq.getTenantId());
            CustomerLimitInfoRes customerLimitInfoRes = BeanMapping.copy(returnCmCustomerLimitInfo, CustomerLimitInfoRes.class);
            returnList.add(customerLimitInfoRes);
            return returnList;
        } else if ("A".equals(creditFreezeReq.getFlag())) {
            //调额请求
            //检查调整后的额度是否大于等于当前已用额度，不满足则不允许调额。
            if (new BigDecimal(creditFreezeReq.getLimitAmount()).compareTo(cmCustomerLimitInfo.getLimitUsed()) < 0) {
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            //获取子节点的修改实体
            CmCustomerLimitInfo updateLimitInfo = this.getCreditAdjustInfoModalOfCms(creditFreezeReq, creditFreezeReq.getOrganizationId(), cmCustomerLimitInfo, creditFreezeReq.getCustomerId(),
                    creditFreezeReq.getLimitId(), creditFreezeReq.getChannel(), creditFreezeReq.getUserId());
            //子节点额度修改记录放入调额记录
            limitAdjustLogList.add(this.buildCmLimitAdjustLogOfCms(creditFreezeReq, cmCustomerLimitInfo, creditFreezeReq.getUserId(), businessDate));
            list.add(updateLimitInfo);

            List<PrCreditLimit> prCreditLimitList = this.getCreaditLimit(updateLimitInfo.getOrganizationId(), updateLimitInfo.getTemplateId(), updateLimitInfo.getTenantId());
            //获取map<limitId ,节点对象>
            Map<String, PrCreditLimit> prCreditLimitMap = prCreditLimitList.stream().collect(Collectors.toMap(PrCreditLimit::getLimitId, Function.identity()));

            //根据客户查询客户所有额度信息，父节点需要按照子节点调整
            //获取客户已有额度集合
            List<CmCustomerLimitInfo> allCustLimits = iCmCustomerLimitInfoService.selectCusLimitListByCustId(updateLimitInfo.getOrganizationId(), updateLimitInfo.getCustomerId(), updateLimitInfo.getTenantId());
            //删除原调额子节点信息，将最新的子节点信息放到集合中
            allCustLimits.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(updateLimitInfo.getLimitId()));
            allCustLimits.add(updateLimitInfo);

            Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = allCustLimits.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

            //根据模版对象 判断节点是否存在父节点
            String fatherLimitId = updateLimitInfo.getFatherId();
            //循环处理父节点
            while (StringUtils.isNotBlank(fatherLimitId)) {
                PrCreditLimit prCreditLimit = prCreditLimitMap.get(fatherLimitId);//父节点
                //原父节点额度信息
                List<CmCustomerLimitInfo> fatherlist = customerLimitInfoMap.get(prCreditLimit.getLimitId());
                //校验父节点信息
                this.checkCmCustomerLimitStatus(fatherlist.get(0), "");

                //父节点改造，按照额度计算方式从新进行计算
                CmCustomerLimitInfo fatherLimitInfo = this.addFatherLimitInfo(updateLimitInfo, prCreditLimit, allCustLimits,customerLimitInfoMap);
                fatherLimitInfo.setUpdateBy(creditFreezeReq.getUserId());
                list.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
                if(!fatherLimitInfo.getOcFlag().equals(CustomerConstant.OC_FLAG_0)){
                    list.add(fatherLimitInfo);
                }

                allCustLimits.removeIf(CmCustomerLimitInfo -> CmCustomerLimitInfo.getLimitId().equals(fatherLimitInfo.getLimitId()));
                allCustLimits.add(fatherLimitInfo);

                customerLimitInfoMap = allCustLimits.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

                //重新修改 fatherLimitId 循环
                fatherLimitId = prCreditLimit.getFatherLimitId();
            }
            //调用修改额度方法
            iCmCustomerLimitInfoService.modifyCustomerLimitList(list, limitAdjustLogList);
            List<CmCustomerLimitInfo> returnCustLimits =
                    iCmCustomerLimitInfoService.selectCusLimitListByCustId(updateLimitInfo.getOrganizationId(), updateLimitInfo.getCustomerId(), updateLimitInfo.getTenantId());
            List<CustomerLimitInfoRes> res = BeanMapping.copyList(returnCustLimits, CustomerLimitInfoRes.class);
            return res;
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelCredit(CreditCancelReq creditCancelReq) throws TxnException {
        CmCustomerSecondLevel cmCustomerSecondLevel = iCustomerService.getSecondLevelByCondition(creditCancelReq.getUserId(), creditCancelReq.getChannel(), creditCancelReq.getTenantId(), null);
        if (cmCustomerSecondLevel == null) {
            logger.info(">>>>取消授信<<<<客户二级信息未查询到数据,userId:{},channel:{},tenantId:{}", creditCancelReq.getUserId(), creditCancelReq.getChannel(), creditCancelReq.getTenantId());
            throw new TxnException("未查询到客户二级信息");
        }
        // 1、通过产品号productId和渠道号channel获取额度节点映射表pr_credit_limit_mapping映射的额度节点limitId
        String limitId = iCreditLimitMappingService.getLimitId(cmCustomerSecondLevel.getOrganizationId(), creditCancelReq.getChannel(), creditCancelReq.getProductId(), creditCancelReq.getTenantId());
        if (StringUtils.isBlank(limitId)) {
            logger.info(">>>>取消授信<<<<映射的额度节点未查询到数据，productId:{},channel:{}", creditCancelReq.getProductId(), creditCancelReq.getChannel());
            throw new TxnException("未查询到映射的额度节点信息");
        }
        // 2、通过额度节点limitId查询额度节点表pr_credit_limit_type获取额度节点,获取额度节点的使用方式useMode
        String useMode = iCreditLimitTypeService.getUserModal(limitId, cmCustomerSecondLevel.getOrganizationId(),creditCancelReq.getTenantId());
        if (StringUtils.isBlank(useMode)) {
            logger.info(">>>>取消授信<<<<额度节点未查询到数据，limitId:{}", limitId);
            throw new TxnException("未查询到额度节点信息");
        }
        // 3、如果为循环额度根据客户号，渠道，额度节点，额度状态查询客户额度信息，否则需要校验外部合同号是否上送，如果没有返回错误信息。
        //    如果上送则根据客户号，渠道，额度节点，外部合同号，额度状态查询客户额度，如果都未能查出客户额度，返回错误信息。
        CmCustomerLimitInfo cmCustomerLimitInfo;
        if (CustomerConstant.USER_MODEL_0.equals(useMode)) {
            cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCancelCreditCustomerLimitInfo(cmCustomerSecondLevel.getCustomerId(), creditCancelReq.getTenantId(), creditCancelReq.getChannel(), limitId, CustomerConstant.STATUS_NORMAL, null);
        } else {
            if (StringUtils.isBlank(creditCancelReq.getContractNumber())) {
                logger.info(">>>>取消授信<<<<非循环额度，外部合同号未上送，productId:{},channel:{}", creditCancelReq.getProductId(), creditCancelReq.getChannel());
                throw new TxnException("非循环额度，外部合同号未上送");
            }
            cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCancelCreditCustomerLimitInfo(cmCustomerSecondLevel.getCustomerId(), creditCancelReq.getTenantId(), creditCancelReq.getChannel(), limitId, CustomerConstant.STATUS_NORMAL, creditCancelReq.getContractNumber());
        }
        // 4、判断客户额度已用额度是否为0，如果是则更新客户额度为失效，否则返回错误信息。
        if (cmCustomerLimitInfo == null) {
            logger.info(">>>>取消授信<<<<客户额度信息未查询到数据");
            throw new TxnException("未查询到客户额度信息");
        }
        if (BigDecimal.ZERO.compareTo(cmCustomerLimitInfo.getLimitUsed()) != 0) {
            logger.info(">>>>取消授信<<<<客户已使用额度不为0,limitUsed:{}", cmCustomerLimitInfo.getLimitUsed());
            throw new TxnException("客户已使用额度不为0");
        }
        iCmCustomerLimitInfoService.cancelCreditModifyCustomerLimitInfo(cmCustomerLimitInfo);
    }

    @Override
    public void creditActive(CreditActiveReq creditActiveReq) throws TxnException {
        CmCustomerSecondLevel cmCustomerSecondLevel = iCustomerService.getSecondLevelByCondition(creditActiveReq.getUserId(), creditActiveReq.getChannel(), creditActiveReq.getTenantId(), null);
        if (cmCustomerSecondLevel == null) {
            logger.info("额度激活，未查询到客户二级信息,userId:{},channel:{},tenantId:{}", creditActiveReq.getUserId(), creditActiveReq.getChannel(), creditActiveReq.getTenantId());
            throw new TxnException("未查询到客户二级信息");
        }

        List<CmCustomerLimitInfo> updateList = new ArrayList<>();

        if(CollectionUtils.isNotEmpty(creditActiveReq.getCreditInfoList())){

            for(CreditInfo creditInfo : creditActiveReq.getCreditInfoList()){
                String limitId = iCreditLimitMappingService.getLimitId(cmCustomerSecondLevel.getOrganizationId(), creditActiveReq.getChannel(), creditInfo.getProductId(), creditActiveReq.getTenantId());
                if (StringUtils.isBlank(limitId)) {
                    logger.info("额度激活,映射的额度节点未查询到数据，productId:{},channel:{}", creditInfo.getProductId(), creditActiveReq.getChannel());
                    throw new TxnException("未查询到映射的额度节点信息");
                }

                String useMode = iCreditLimitTypeService.getUserModal(limitId, cmCustomerSecondLevel.getOrganizationId(),creditActiveReq.getTenantId());
                if (StringUtils.isBlank(useMode)) {
                    logger.info("额度激活,额度节点未查询到数据，limitId:{}", limitId);
                    throw new TxnException("未查询到额度节点信息");
                }

                CmCustomerLimitInfo cmCustomerLimitInfo;
                if (CustomerConstant.USER_MODEL_0.equals(useMode)) {
                    cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCancelCreditCustomerLimitInfo(cmCustomerSecondLevel.getCustomerId(), creditActiveReq.getTenantId(), creditActiveReq.getChannel(), limitId, CustomerConstant.STATUS_NOT_ACTIVE, null);
                } else {
                    if (StringUtils.isBlank(creditInfo.getContractNumber())) {
                        logger.info("额度激活,非循环额度，外部合同号未上送，productId:{},channel:{}", creditInfo.getProductId(), creditActiveReq.getChannel());
                        throw new TxnException("非循环额度，外部合同号未上送");
                    }
                    cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCancelCreditCustomerLimitInfo(cmCustomerSecondLevel.getCustomerId(), creditActiveReq.getTenantId(), creditActiveReq.getChannel(), limitId, CustomerConstant.STATUS_NOT_ACTIVE, creditInfo.getContractNumber());
                }

                if (cmCustomerLimitInfo==null) {
                    logger.error(ErrorMsgConstant.CUSTOMER_QUERY_NODATA);
                    throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
                }
                CmCustomerLimitInfo updateDate = new CmCustomerLimitInfo();
                updateDate.setId(cmCustomerLimitInfo.getId());
                updateDate.setTenantId(cmCustomerLimitInfo.getTenantId());
                updateDate.setCustomerId(cmCustomerLimitInfo.getCustomerId());
                updateDate.setOcFlag(CustomerConstant.STATUS_NORMAL);
                updateDate.setLimitStatus(CustomerConstant.STATUS_NOT_ACTIVE);
                updateDate.setUpdateBy(creditActiveReq.getUserId());

                updateList.add(updateDate);
            }
        }
        if(CollectionUtils.isNotEmpty(updateList)){
            for(CmCustomerLimitInfo updateLimit : updateList){
                extCmCustomerLimitInfoMapper.updateCustomerLimitStatus(updateLimit);
            }
        }


    }

    @Override
    public CreditReferenceRes creditReference(CreditReferenceReq creditReferenceReq) throws TxnException {
        CmCustomerSecondLevel cmCustomerSecondLevel = extCmCustomerSecondLevelMapper.selectByUserIdAndChannel(creditReferenceReq.getUserId(), creditReferenceReq.getChannelNo(), creditReferenceReq.getTenantId());
        if (cmCustomerSecondLevel == null) {
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getMsg());
        }
        //获取综合额度信息
        CmLimitChangeLog cmLimitChangeLog = extCmLimitChangeLogMapper.selectLimitOneByProductIdAndCustomerId(creditReferenceReq.getTenantId(), creditReferenceReq.getChannelNo(), creditReferenceReq.getProductId(), cmCustomerSecondLevel.getCustomerId());
        CreditReferenceRes creditReferenceRes = new CreditReferenceRes();
        if (null != cmLimitChangeLog) {
            //综合额度存在
            creditReferenceRes.setEtlExistFlag("1");
            //综合额度到期日期
            creditReferenceRes.setEtlExpiryDate(cmLimitChangeLog.getEtlExpiryDate());
            PrOrganizationTable org = extPrOrganizationTableMapper.selectByOrganizationId(cmCustomerSecondLevel.getOrganizationId(), cmCustomerSecondLevel.getTenantId());
            Date nextProcessingDate = org.getNextProcessingDate();
            List<String> xahSeriesProducts = BizTypeProducts.getXAHSeriesProducts();
            //获取客户相关的所有额度编号，做集合运算  兴安花系列产品对应的额度列表
            List<String> limitIds = extPrCreditLimitMappingMapper.selectLimitIdByTenIdAndProductIds(creditReferenceReq.getTenantId(), xahSeriesProducts);
            List<CmCustomerLimitInfo> cmCustomerLimitInfoList = extCmCustomerLimitInfoMapper.selectCustLimits(cmCustomerSecondLevel.getCustomerId(), creditReferenceReq.getTenantId(), cmCustomerSecondLevel.getOrganizationId(), limitIds, Arrays.asList("1", "2"));
            //获取未结清数据
            CmCustomerLimitInfo customerLimitInfo = cmCustomerLimitInfoList.stream().filter(cmCustomerLimitInfo -> !cmCustomerLimitInfo.getLimitExpiryDate().before(nextProcessingDate) || 0 != cmCustomerLimitInfo.getLimitAvailable().compareTo(cmCustomerLimitInfo.getLimitAmount())).findFirst().orElse(null);
            if (null != customerLimitInfo) {
                //未结清
                creditReferenceRes.setEtlSettleFlag("0");
            } else {
                //已结清
                creditReferenceRes.setEtlSettleFlag("1");
                EtlSettleReq req = new EtlSettleReq();
                req.setCustomerId(cmCustomerSecondLevel.getCustomerId());
                req.setTenantId(cmCustomerSecondLevel.getTenantId());
                req.setProducts(xahSeriesProducts);
                //获取结清日期
                TxnRespResult<EtlSettleRes> respResult = anyTxnTransactionService.getEtlSettleDate(req);
                if (null == respResult || 1000 != respResult.getCode()) {
                    logger.error("客户->{}获取结清日期失败", cmCustomerSecondLevel.getCustomerId());
                    throw new TxnBizException(TxnRespCode.ETL_SETTLE_DATE_ERROR.getCode(),
                            TxnRespCode.ETL_SETTLE_DATE_ERROR.getUserTips());
                } else {
                    Date etlSettleDate = respResult.getData().getEtlSettleDate();
                    if (null == etlSettleDate) {
                        etlSettleDate = cmLimitChangeLog.getEtlSettleDate();
                    }
                    // 还是空的话（切日后半小时误差）
                    if (null == etlSettleDate) {
                        etlSettleDate = org.getToday();
                    }
                    creditReferenceRes.setEtlSettleDate(etlSettleDate);
                }
            }

        } else {
            //综合额度不存
            creditReferenceRes.setEtlExistFlag("0");
        }

        return creditReferenceRes;
    }

    /**
     * cms客户额度注销校验
     *
     * @param creditFreezeReq
     * @return
     */
    @Override
    public CustomerLimitAdjustRes cancelLimitVerify(CreditFreezeOfCmsReq creditFreezeReq) throws TxnBizException {

        CustomerLimitAdjustRes res = new CustomerLimitAdjustRes();
        String tenantId = creditFreezeReq.getTenantId();
        String organizationId = creditFreezeReq.getOrganizationId();
        String customerId = creditFreezeReq.getCustomerId();
        String limitId = creditFreezeReq.getLimitId();
        String channel = creditFreezeReq.getChannel();

        // 校验额度是否是非注销状态
        CmCustomerLimitInfo cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByTenIdAndCusIdAndLimitId(tenantId, customerId, limitId);
        // 额度当前状态
        String limitStatus = cmCustomerLimitInfo.getLimitStatus();
        if (CustomerConstant.CREDIT_LIMIT_STATUS_4.equalsIgnoreCase(limitStatus)) {
            res.setMsg("用户当前状态为已注销");
            return res;
        }
        // 授信合同号
        String contractNumber = cmCustomerLimitInfo.getContractNumber();
        CmLimitChangeLog queryCon = new CmLimitChangeLog();
        queryCon.setTenantId(tenantId);
        queryCon.setCustomerId(customerId);
        List<CmLimitChangeLog> queryLogs = extCmLimitChangeLogMapper.queryByCon(queryCon);
        Optional<CmLimitChangeLog> optional = queryLogs.stream()
                //.filter(log -> Objects.isNull(log.getEtlSettleDate()))
                .filter(log -> contractNumber.equalsIgnoreCase(log.getContractNumber()))
                .findAny();
        if (!optional.isPresent()) {
            res.setMsg("未查询到客户有效的综合额度信息，无法注销");
            return res;
        }

        // 校验产品需激活状态
        CmLimitChangeLog changeLog = optional.get();
        String productId = changeLog.getProductId();
        List<String> productIds = BizTypeProducts.getSeriesProductIds(productId);
        List<CmCustomerExtInfo> cmCustomerExtInfos = extCmCustomerExtInfoMapper.queryNotActiveCustomers(customerId, tenantId, Constant.YES_FLAG, productIds);
        List<String> activeIds = cmCustomerExtInfos.stream().map(CmCustomerExtInfo::getProductId).collect(Collectors.toList());
        boolean match = cmCustomerExtInfos.stream().anyMatch(info -> productId.equalsIgnoreCase(info.getProductId()));
        if (!match) {
            res.setMsg("客户对应的产品未激活，无法注销");
            return res;
        }
        Predicate<CmLimitChangeLog> logPredicate = log -> productIds.contains(log.getProductId()) && activeIds.contains(log.getProductId());
        /*PrOrganizationTable org = organizationTableService.findByOrId(organizationId, tenantId);
        // 当前业务日期
        Date businessDate = org.getNextProcessingDate();
        Date maxDate = queryLogs.stream()
                .filter(logPredicate)
                .max(Comparator.comparing(CmLimitChangeLog::getEtlExpiryDate))
                .map(CmLimitChangeLog::getEtlExpiryDate)
                .get();
        if (businessDate.compareTo(maxDate) >= 0) {
            res.setMsg("当前日期大于等于最大的综合额度有效期，无法注销");
            return res;
        }*/

        // 3.校验已使用的额度为0
        List<String> contractNumbers = queryLogs.stream().filter(logPredicate).map(CmLimitChangeLog::getContractNumber).collect(Collectors.toList());

        List<CmCustomerLimitInfo> customerLimitInfos = extCmCustomerLimitInfoMapper.selectCusLimitListByCusId(organizationId, customerId, tenantId);
        boolean limitUsed = customerLimitInfos.stream()
                .filter(info -> contractNumbers.contains(info.getContractNumber()))
                .anyMatch(info -> BigDecimal.ZERO.compareTo(info.getLimitUsed()) < 0 || info.getLimitAvailable().compareTo(info.getLimitAmount()) != 0);
        if (limitUsed) {
            res.setMsg("当前客户有欠款或额度值不正确，无法注销");
            return res;
        }
        BillReq billReq = new BillReq();
        //渠道号
        billReq.setChannel(channel);
        //流水号
        billReq.setSeqNo(SeqUtils.getRandomNumber(18));
        //租户id
        billReq.setTenantId(tenantId);
        //用户id
        billReq.setUserId(cmCustomerExtInfos.get(0).getUserId());
        BizTypeEnum bizType = BizTypeProducts.getBizType(productId);
        //设置业务类型
        billReq.setBizType(bizType.getCode());
        // 4.校验客户没有待还账单的
        TxnRespResult<AllRemainBillQueryRes> respResult = anyTxnTransactionService.allRemainBillQuery(billReq);
        BigDecimal amount = BigDecimal.ZERO;
        if (null == respResult || 1000 != respResult.getCode()) {
            res.setMsg("查询账单失败，无法注销");
            return res;
        } else {
            AllRemainBillQueryRes bill = respResult.getData();
            BigDecimal totalAmount = bill.getTotalAmount();
            amount = amount.add(Objects.isNull(totalAmount) ? BigDecimal.ZERO : totalAmount);
        }
        if (BigDecimal.ZERO.compareTo(amount) < 0) {
            res.setMsg("当前有欠款，无法注销");
            return res;
        }
        // 有用信未供数的不能当天注销（当天激活当天注销）
        TxnRespResult<Boolean> loanRecordResult = anyTxnTransactionService.queryLoanRecord(billReq);
        if (null == loanRecordResult || 1000 != loanRecordResult.getCode()) {
            res.setMsg("查询是否用信失败，无法注销");
            return res;
        } else {
            Boolean loanRecordFlag = loanRecordResult.getData();
            if (loanRecordFlag && StringUtils.isBlank(changeLog.getEtlLoanId())) {
                res.setMsg("有用信且未供数，无法注销");
                return res;
            }
        }
        res.setCancelFlag(Constant.YES_FLAG);
        res.setMsg("可以注销");
        return res;
    }

    private void insertCmLimitAdjustLog(BtLimitAdjustInfo btLimitAdjustInfo,String errorMessage,Date busiDate){
        CmLimitAdjustLog cmLimitAdjustLog = BeanMapping.copy(btLimitAdjustInfo, CmLimitAdjustLog.class);
        try {
            cmLimitAdjustLog.setId(iSegmentService.getId(Constant.CM_LIMIT_ADJUST_LOG));
        } catch (SegmentException e) {
            logger.error("主键生成错误 ");
        }
        cmLimitAdjustLog.setResult(CustomerConstant.FAIL);
        cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_0);//调额
        cmLimitAdjustLog.setAdjustReason("批量调额");
        cmLimitAdjustLog.setDescription(errorMessage);
        cmLimitAdjustLog.setBusinessDate(busiDate);

        extCmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);

    }

    /**
     * mq发送联合贷授信申请
     *
     * @param investorCreditBeans 联合贷授信申请list
     * @param bean 授信处理bean
     * @throws Exception
     * @author liuyang
     * @date 2020-6-17
     */
    private void sendInvestorCreditMessage(List<InvestorCreditBean> investorCreditBeans, CreditApprovalBean bean) throws Exception {
        String jsonMessage = JsonUtils.toJSon(investorCreditBeans);
        String jsonData = JsonUtils.toJSon(bean);
        logger.info("开始发送MQ消息，联合贷授信申请信息：{}，写表数据：{}",jsonMessage, jsonData);
//        SendResult sendResult = rocketMQTemplate.syncSend(CustomerConstant.MQ_INVESTOR_CREDIT_TOPIC,jSonMessage);
        /*
         * 目前这边事务MQ只有一处地方
         * 之后其他地方使用到事务MQ时，需要加入用于判断业务的参数
         * 在监听事务MQ的地方，通过判断该参数的值，进行不同的处理
         * 参数可以放入Message中
         */
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(CustomerConstant.MQ_INVESTOR_CREDIT_TOPIC + grayVersionConfig.getCurrentVersion(), MessageBuilder.withPayload(jsonMessage).build(), bean);
        logger.info("联合贷授信申请，发送MQ消息结果：{}",transactionSendResult);
        if (transactionSendResult.getSendStatus() != SendStatus.SEND_OK) {
            throw new TxnException("联合贷授信申请MQ发送失败");
        }
    }

    private CmLimitAdjustLog createCmLimitAdjustLog(String seq, CreditFreeze creditFreeze, CmCustomerLimitInfo cmCustomerLimitInfo, String userId, Date businessDate, String adjustReason) {
        CmLimitAdjustLog cmLimitAdjustLog = new CmLimitAdjustLog();
        //主键
        try {
            cmLimitAdjustLog.setId(iSegmentService.getId(Constant.CM_LIMIT_ADJUST_LOG));
        } catch (SegmentException e) {
            logger.error("主键生成错误 ");
        }
        //租户ID
        cmLimitAdjustLog.setTenantId(cmCustomerLimitInfo.getTenantId());
        // 流水号
        cmLimitAdjustLog.setSeqNo(seq);
        //客户id
        cmLimitAdjustLog.setCustomerId(cmCustomerLimitInfo.getCustomerId());
        //业务机构id
        cmLimitAdjustLog.setOrganizationId(cmCustomerLimitInfo.getOrganizationId());
        //额度编号
        cmLimitAdjustLog.setLimitId(cmCustomerLimitInfo.getLimitId());
        //额度模板id
        cmLimitAdjustLog.setTemplateId(cmCustomerLimitInfo.getTemplateId());
        //调额前永久额度
        cmLimitAdjustLog.setLimitPermBefore(cmCustomerLimitInfo.getLimitPerm());
        //调整前临时额度失效日期
        cmLimitAdjustLog.setLimitTempEndDateBefore(cmCustomerLimitInfo.getLimitExpiryDate());
        // 调整前临时额度生效日期
        cmLimitAdjustLog.setLimitTempStartDateBefore(cmCustomerLimitInfo.getLimitEffectiveDate());
        //渠道号
        cmLimitAdjustLog.setChannel(cmCustomerLimitInfo.getChannel());
        //来源 	1.批量调额 2.联机调额
        cmLimitAdjustLog.setDataSource(CustomerConstant.DATASOURCE_2);
        //处理结果 S = 成功 F = 失败CustomerConstant.SUCCESS
        cmLimitAdjustLog.setResult(CustomerConstant.SUCCESS);
        if("F".equals(creditFreeze.getOperateType())){
            cmLimitAdjustLog.setDescription("冻结");
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_1);
        }else{
            cmLimitAdjustLog.setDescription("解冻");
            cmLimitAdjustLog.setAdjustType(CustomerConstant.ADJUST_TYPE_2);
        }
        cmLimitAdjustLog.setAdjustReason(null == adjustReason ? "" : adjustReason);
        //最后更新操作员
        cmLimitAdjustLog.setUpdateBy(userId);
        //业务日期
        cmLimitAdjustLog.setBusinessDate(businessDate);
        //记录版本号
        cmLimitAdjustLog.setRecordVersionNumber(Constant.VERSION_NUMBER);
        return cmLimitAdjustLog;
    }


    /**
     * 赋值生成CustomerLimitInfo
     *
     * @author Qingyao Xing
     * @date 2019/8/5 16:26
     */
    private CmCustomerLimitInfo addCmCustomerLimitInfo(CmCustomerLimitInfo info, BigDecimal limit, BigDecimal limitAvailable, PrCreditLimit prCreditLimit) {
        CmCustomerLimitInfo limitInfo = BeanMapping.copy(info, CmCustomerLimitInfo.class);
        //授信额度--请求报文（授信金额）
        limitInfo.setLimitAmount(limit);
        //可用额度--请求报文（授信金额）
        limitInfo.setLimitAvailable(limitAvailable);
        //永久额度--请求报文（授信金额）
        limitInfo.setLimitPerm(limit);

        //添加默认值
        //流水号（默认为空）
        limitInfo.setSeqNo("");
        //已使用额度 (授信是为0)
        limitInfo.setLimitUsed(BigDecimal.ZERO);
        //临时额度 (授信是为0)
        limitInfo.setLimitTemp(BigDecimal.ZERO);
        //实时还款未入账金额 (授信是为0)
        limitInfo.setOnlinePaymentAmount(BigDecimal.ZERO);
        //版本号 (默认为1)
        limitInfo.setRecordVersionNumber(Constant.VERSION_NUMBER);

        if(Constant.NO_FLAG.equals(prCreditLimit.getNeedActive())){
            //0 = 否 授信即激活
            limitInfo.setLimitStatus(CustomerConstant.STATUS_NORMAL);
        }else{
            //1 = 是 授信后为未激活状态
            limitInfo.setLimitStatus(CustomerConstant.STATUS_NOT_ACTIVE);
        }

        //添加来自节点模版的数据

        //检核标识 --(额度模板表:检核标识)
        limitInfo.setAuthCheck(prCreditLimit.getAuthCheck());
        //继承比例--(额度模板表:占比)
        limitInfo.setObtainPercentage(prCreditLimit.getPercentage());
        //额度计算方式 0 输入 1 求和 2 最大值 3 最小值 (子节点都是按照输入的额度)
        limitInfo.setOcFlag(prCreditLimit.getOcFlag());
        //父节点额度编号--(额度模板表:上层额度节点id)
        limitInfo.setFatherId(prCreditLimit.getFatherLimitId());

        //添加来自节点的数据
        //中文名称--额度节点表(描述)
        limitInfo.setChineseName(prCreditLimit.getLimitDesc());
        //额度属性--额度节点表(使用方式)
        limitInfo.setUseMode(prCreditLimit.getUseMode());
        //额度类型--额度节点表(额度种类)
        limitInfo.setLimitClass(prCreditLimit.getLimitClass());
        //额度编号--额度节点表(额度节点id)
        limitInfo.setLimitId(prCreditLimit.getLimitId());

        return limitInfo;
    }

    /**
     * 生成营销记录
     *
     * @param customerSecondLevel api传递数据
     * @return 营销记录
     */
    private TlMarketingRecord buildTlMarketingRecord(CmCustomerFirstLevel customerFirstLevel, CmCustomerSecondLevel customerSecondLevel, List<CmCustomerAddressInfo> addressInfoList
            , List<CmCustomerLimitInfo> customerLimitInfoList, String step, String seqNo) throws TxnBizException {
        //查询客户地址信息
        CmCustomerAddressInfo cond = new CmCustomerAddressInfo();
        cond.setAddressStatus(CustomerConstant.STATUS_NORMAL);
        cond.setCustomerId(customerSecondLevel.getCustomerId());
        cond.setChannel(customerSecondLevel.getChannel());
        cond.setTenantId(customerSecondLevel.getTenantId());
        addressInfoList = extCmCustomerAddressInfoMapper.selectByCondSelective(cond);

        MarketingRecordCreationBean marketingRecordCreationBean = new MarketingRecordCreationBean();
        marketingRecordCreationBean.setCustomerFirstLevel(customerFirstLevel);
        marketingRecordCreationBean.setCustomerSecondLevel(customerSecondLevel);
        marketingRecordCreationBean.setAddressInfoList(addressInfoList);
        marketingRecordCreationBean.setCustomerLimitInfoList(customerLimitInfoList);
        marketingRecordCreationBean.setStep(step);
        marketingRecordCreationBean.setSeqNo(seqNo);
        TlMarketingRecord result = iCustomerService.generateMarketingRecord(marketingRecordCreationBean);

        if(null != result){
            try {
                result.setId(iSegmentService.getId(Constant.TL_MARKETING_RECORD));
            } catch (SegmentException e) {
                logger.error("主键生成错误", e);
                throw new TxnBizException(ErrorMsgConstant.ERROR_ID);
            }
        }
        return result;
    }


    /**
     * 获取年利率
     * @param secondLevel 客户二级信息
     * @param product 产品信息
     * @return
     */
    private BigDecimal getInterest(CustomerSecondLevelRes secondLevel, PrProductTable product){
        BigDecimal yearRate = BigDecimal.ZERO;

        //修改用户客群获取方式
        CmCustomerExtInfo info = new CmCustomerExtInfo();
        info.setChannel(secondLevel.getChannel());
        info.setTenantId(secondLevel.getTenantId());
        info.setCustomerId(secondLevel.getCustomerId());
        info.setProductId(product.getProductId());
        String customerGroup = secondLevel.getCustomerGroup();
        CmCustomerExtInfo res = extCmCustomerExtInfoMapper.selectByConditions(info);
        if(Objects.nonNull(res)){
            customerGroup = res.getCustomerGroup();
        }
        //规则匹配相关参数
        Map<String, Object> map = new HashMap<>();
        map.put("transactionChannels", secondLevel.getChannel());//渠道
        map.put("customerIdentification", customerGroup);//客群
        RuleMatchReq ruleMatchReq = new RuleMatchReq(secondLevel.getOrganizationId(), product.getProductId(),
                "InterestMatchRuleSet", map, secondLevel.getTenantId());
        try {
            String interestId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get("tableId");
            PrInterestTable interestTable = interestTableService.findInterestByInterestId(interestId, secondLevel.getTenantId());
            yearRate = interestTableService.getYearRate(interestTable); // 年利率
        }catch (Exception ex){
            logger.error("查询客户年利率异常{}", ex.getMessage());
        }
       return yearRate;
    }
}
